mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-06 20:52:09 +00:00
Compare commits
128 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a07fa8ccd2 | ||
|
|
c77f32e696 | ||
|
|
4391771416 | ||
|
|
01ab820459 | ||
|
|
c7218f2856 | ||
|
|
592221b4bf | ||
|
|
836458ad85 | ||
|
|
7233c86f3d | ||
|
|
154b1b05e4 | ||
|
|
4d88638d4d | ||
|
|
9403986643 | ||
|
|
ad48610b2f | ||
|
|
63487cf3ec | ||
|
|
4ae2087c2e | ||
|
|
5179129a6b | ||
|
|
6a00d8c88c | ||
|
|
50f43f9396 | ||
|
|
6f205f8931 | ||
|
|
3776ffa49b | ||
|
|
318b5beac1 | ||
|
|
344c5d6d12 | ||
|
|
4d319a8caa | ||
|
|
74b24a0690 | ||
|
|
1ca0464957 | ||
|
|
f7ebc8a88c | ||
|
|
a102099ac1 | ||
|
|
59ac22aa05 | ||
|
|
cd7244b3d7 | ||
|
|
f81b676abe | ||
|
|
234b154053 | ||
|
|
a1d09ad574 | ||
|
|
b983b23e7e | ||
|
|
0b81e77a94 | ||
|
|
b8cf314bfe | ||
|
|
4a3338e59c | ||
|
|
5b8538c0f4 | ||
|
|
88d6320d08 | ||
|
|
651c9c2c9b | ||
|
|
8dd45cd388 | ||
|
|
126ac354d5 | ||
|
|
024769c402 | ||
|
|
5acf141669 | ||
|
|
92e3e8ab7b | ||
|
|
187a29c666 | ||
|
|
4c24631795 | ||
|
|
7d6bd10cca | ||
|
|
f8c86769a7 | ||
|
|
e0b0dda382 | ||
|
|
b8708f086e | ||
|
|
3539e4dce9 | ||
|
|
5fdadcf557 | ||
|
|
acb3f01f79 | ||
|
|
83becdb19d | ||
|
|
e3e8fe7895 | ||
|
|
6ddff8fae1 | ||
|
|
f5cb2dbdcf | ||
|
|
fe19769d82 | ||
|
|
45e404b15b | ||
|
|
5bdaa68368 | ||
|
|
d903a377bf | ||
|
|
8e7745f4c1 | ||
|
|
a9ea6330d9 | ||
|
|
bfb0260550 | ||
|
|
bba1cb3832 | ||
|
|
29ad2144b7 | ||
|
|
38d367e709 | ||
|
|
0e81ff970f | ||
|
|
00feef40a3 | ||
|
|
dfba593072 | ||
|
|
c770c8d988 | ||
|
|
0f071031a9 | ||
|
|
99efa857f4 | ||
|
|
80035395ff | ||
|
|
d3490e1c95 | ||
|
|
dab13c92eb | ||
|
|
1f18542960 | ||
|
|
8f21ea9367 | ||
|
|
73e64d9052 | ||
|
|
6cdd87da41 | ||
|
|
2a5d49f9b3 | ||
|
|
cc7ba9eb9f | ||
|
|
c4cc42c8d5 | ||
|
|
2d0838b112 | ||
|
|
07b94a8e48 | ||
|
|
4b08abc144 | ||
|
|
93e4fc2f32 | ||
|
|
6dd86eec30 | ||
|
|
a7ab5d55d3 | ||
|
|
689547463c | ||
|
|
8a0046c571 | ||
|
|
fb3991321a | ||
|
|
ca6543a919 | ||
|
|
364a6aa3a2 | ||
|
|
82b0667277 | ||
|
|
74c126c731 | ||
|
|
d87a0fe74f | ||
|
|
9a858f628d | ||
|
|
fed01fa9d2 | ||
|
|
e1468da36a | ||
|
|
ddfc1440cd | ||
|
|
5fc46384e6 | ||
|
|
48d9df1e43 | ||
|
|
6b62d91f82 | ||
|
|
e6ca8cd167 | ||
|
|
059748ad3b | ||
|
|
a334f998a2 | ||
|
|
53a5ccef31 | ||
|
|
9eea73cefb | ||
|
|
b210e1f243 | ||
|
|
ef3202101c | ||
|
|
22d5159d16 | ||
|
|
1cbd30bd9e | ||
|
|
ad54358de7 | ||
|
|
3689b58b92 | ||
|
|
5a4180a750 | ||
|
|
047922b13a | ||
|
|
798d747164 | ||
|
|
29676ffb22 | ||
|
|
8a50b063d4 | ||
|
|
3d2444ab2e | ||
|
|
7c395edab4 | ||
|
|
7e7f322e21 | ||
|
|
9350fb4b97 | ||
|
|
59c3cc6ce1 | ||
|
|
d7001937ac | ||
|
|
3fe58ec66b | ||
|
|
ed12f73483 | ||
|
|
f357f40fc7 |
21
README.md
21
README.md
@@ -10,6 +10,17 @@ No vendor lock-in, which means that all the configuration for your applications/
|
||||
|
||||
For more information, take a look at our landing page [here](https://coolify.io).
|
||||
|
||||
# Installation
|
||||
|
||||
```bash
|
||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||
```
|
||||
You can find the installation script source [here](./scripts/install.sh).
|
||||
|
||||
# Support
|
||||
|
||||
Contact us [here](https://coolify.io/docs/contact).
|
||||
|
||||
# Donations
|
||||
To stay completely free, open-source, no feature behind paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the future development of the project.
|
||||
|
||||
@@ -66,16 +77,6 @@ By subscribing to the cloud version, you get the Coolify server for the same pri
|
||||
- Better support
|
||||
- Less maintenance for you
|
||||
|
||||
# Installation
|
||||
|
||||
```bash
|
||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||
```
|
||||
You can find the installation script source [here](./scripts/install.sh).
|
||||
|
||||
# Support
|
||||
|
||||
Contact us [here](https://coolify.io/docs/contact).
|
||||
|
||||
# Recognitions
|
||||
|
||||
|
||||
@@ -106,7 +106,6 @@ class StartMariadb
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$database_name = addslashes($database->name);
|
||||
$this->commands[] = "echo 'Database started.'";
|
||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||
}
|
||||
|
||||
@@ -122,7 +122,6 @@ class StartMongodb
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$database_name = addslashes($database->name);
|
||||
$this->commands[] = "echo 'Database started.'";
|
||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||
}
|
||||
|
||||
@@ -106,7 +106,6 @@ class StartMysql
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$database_name = addslashes($database->name);
|
||||
$this->commands[] = "echo 'Database started.'";
|
||||
return remote_process($this->commands, $database->destination->server,callEventOnFinish: 'DatabaseStatusChanged');
|
||||
}
|
||||
|
||||
@@ -128,7 +128,6 @@ class StartPostgresql
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$database_name = addslashes($database->name);
|
||||
$this->commands[] = "echo 'Database started.'";
|
||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||
}
|
||||
|
||||
@@ -117,7 +117,6 @@ class StartRedis
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$database_name = addslashes($database->name);
|
||||
$this->commands[] = "echo 'Database started.'";
|
||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||
}
|
||||
|
||||
@@ -128,9 +128,9 @@ class InstallLogDrain
|
||||
if ($type !== 'custom') {
|
||||
$parsers = base64_encode("
|
||||
[PARSER]
|
||||
Name empty_line_skipper
|
||||
Format regex
|
||||
Regex /^(?!\s*$).+/
|
||||
Name empty_line_skipper
|
||||
Format regex
|
||||
Regex /^(?!\s*$).+/
|
||||
");
|
||||
}
|
||||
$compose = base64_encode("
|
||||
@@ -198,7 +198,7 @@ Files:
|
||||
}
|
||||
$restart_command = [
|
||||
"echo 'Stopping old Fluent Bit'",
|
||||
"cd $config_path && docker rm -f coolify-log-drain || true",
|
||||
"cd $config_path && docker compose down --remove-orphans || true",
|
||||
"echo 'Starting Fluent Bit'",
|
||||
"cd $config_path && docker compose up -d --remove-orphans",
|
||||
];
|
||||
|
||||
@@ -25,7 +25,6 @@ class UpdateCoolify
|
||||
CleanupDocker::run($this->server, false);
|
||||
$this->latestVersion = get_latest_version_of_coolify();
|
||||
$this->currentVersion = config('version');
|
||||
ray('latest version:' . $this->latestVersion . " current version: " . $this->currentVersion . ' force: ' . $force);
|
||||
if ($settings->next_channel) {
|
||||
ray('next channel enabled');
|
||||
$this->latestVersion = 'next';
|
||||
@@ -44,7 +43,7 @@ class UpdateCoolify
|
||||
}
|
||||
$this->update();
|
||||
}
|
||||
send_internal_notification('InstanceAutoUpdateJob done to version: ' . $this->latestVersion . ' from version: ' . $this->currentVersion);
|
||||
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
|
||||
} catch (\Throwable $e) {
|
||||
ray('InstanceAutoUpdateJob failed');
|
||||
ray($e->getMessage());
|
||||
|
||||
@@ -45,6 +45,9 @@ class DeleteService
|
||||
foreach ($service->databases()->get() as $database) {
|
||||
$database->forceDelete();
|
||||
}
|
||||
foreach ($service->scheduled_tasks as $task) {
|
||||
$task->delete();
|
||||
}
|
||||
$service->tags()->detach();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,6 @@ class StartService
|
||||
{
|
||||
ray('Starting service: ' . $service->name);
|
||||
$service->saveComposeConfigs();
|
||||
|
||||
$service_name = addslashes($service->name);
|
||||
$server_name = addslashes($service->server->name);
|
||||
|
||||
$commands[] = "cd " . $service->workdir();
|
||||
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
||||
$commands[] = "echo 'Creating Docker network.'";
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ScheduledTask;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
@@ -108,6 +109,17 @@ class CleanupStuckedResources extends Command
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck serviceapp: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$scheduled_tasks = ScheduledTask::all();
|
||||
foreach ($scheduled_tasks as $scheduled_task) {
|
||||
if (!$scheduled_task->service && !$scheduled_task->application) {
|
||||
echo "Deleting stuck scheduledtask: {$scheduled_task->name}\n";
|
||||
$scheduled_task->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck scheduledtasks: {$e->getMessage()}\n";
|
||||
}
|
||||
|
||||
// Cleanup any resources that are not attached to any environment or destination or server
|
||||
try {
|
||||
|
||||
@@ -125,6 +125,9 @@ class Init extends Command
|
||||
// Cleanup any failed deployments
|
||||
|
||||
try {
|
||||
if (isCloud()) {
|
||||
return;
|
||||
}
|
||||
$queued_inprogress_deployments = ApplicationDeploymentQueue::whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])->get();
|
||||
foreach ($queued_inprogress_deployments as $deployment) {
|
||||
ray($deployment->id, $deployment->status);
|
||||
|
||||
@@ -73,6 +73,18 @@ class ServicesGenerate extends Command
|
||||
} else {
|
||||
$slogan = str($file)->headline()->value();
|
||||
}
|
||||
$logo = collect(preg_grep('/^# logo:/', explode("\n", $content)))->values();
|
||||
if ($logo->count() > 0) {
|
||||
$logo = str($logo[0])->after('# logo:')->trim()->value();
|
||||
} else {
|
||||
$logo = 'svgs/unknown.svg';
|
||||
}
|
||||
$minversion = collect(preg_grep('/^# minversion:/', explode("\n", $content)))->values();
|
||||
if ($minversion->count() > 0) {
|
||||
$minversion = str($minversion[0])->after('# minversion:')->trim()->value();
|
||||
} else {
|
||||
$minversion = '0.0.0';
|
||||
}
|
||||
$env_file = collect(preg_grep('/^# env_file:/', explode("\n", $content)))->values();
|
||||
if ($env_file->count() > 0) {
|
||||
$env_file = str($env_file[0])->after('# env_file:')->trim()->value();
|
||||
@@ -96,6 +108,8 @@ class ServicesGenerate extends Command
|
||||
'slogan' => $slogan,
|
||||
'compose' => $yaml,
|
||||
'tags' => $tags,
|
||||
'logo' => $logo,
|
||||
'minversion' => $minversion,
|
||||
];
|
||||
if ($env_file) {
|
||||
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Console;
|
||||
|
||||
use App\Jobs\CheckLogDrainContainerJob;
|
||||
use App\Jobs\CleanupInstanceStuffsJob;
|
||||
use App\Jobs\ComplexContainerStatusJob;
|
||||
use App\Jobs\DatabaseBackupJob;
|
||||
use App\Jobs\ScheduledTaskJob;
|
||||
use App\Jobs\InstanceAutoUpdateJob;
|
||||
@@ -120,8 +119,8 @@ class Kernel extends ConsoleKernel
|
||||
return;
|
||||
}
|
||||
foreach ($scheduled_tasks as $scheduled_task) {
|
||||
$service = $scheduled_task->service()->get();
|
||||
$application = $scheduled_task->application()->get();
|
||||
$service = $scheduled_task->service;
|
||||
$application = $scheduled_task->application;
|
||||
|
||||
if (!$application && !$service) {
|
||||
ray('application/service attached to scheduled task does not exist');
|
||||
|
||||
@@ -18,8 +18,7 @@ class Deploy extends Controller
|
||||
{
|
||||
public function deploy(Request $request)
|
||||
{
|
||||
$token = auth()->user()->currentAccessToken();
|
||||
$teamId = data_get($token, 'team_id');
|
||||
$teamId = get_team_id_from_token();
|
||||
$uuids = $request->query->get('uuid');
|
||||
$tags = $request->query->get('tag');
|
||||
$force = $request->query->get('force') ?? false;
|
||||
|
||||
39
app/Http/Controllers/Api/Project.php
Normal file
39
app/Http/Controllers/Api/Project.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Project as ModelsProject;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Project extends Controller
|
||||
{
|
||||
public function projects(Request $request)
|
||||
{
|
||||
$teamId = get_team_id_from_token();
|
||||
if (is_null($teamId)) {
|
||||
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
|
||||
}
|
||||
$projects = ModelsProject::whereTeamId($teamId)->select('id', 'name', 'uuid')->get();
|
||||
return response()->json($projects);
|
||||
}
|
||||
public function project_by_uuid(Request $request)
|
||||
{
|
||||
$teamId = get_team_id_from_token();
|
||||
if (is_null($teamId)) {
|
||||
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
|
||||
}
|
||||
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']);
|
||||
return response()->json($project);
|
||||
}
|
||||
public function environment_details(Request $request)
|
||||
{
|
||||
$teamId = get_team_id_from_token();
|
||||
if (is_null($teamId)) {
|
||||
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
|
||||
}
|
||||
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
|
||||
$environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']);
|
||||
return response()->json($environment);
|
||||
}
|
||||
}
|
||||
54
app/Http/Controllers/Api/Server.php
Normal file
54
app/Http/Controllers/Api/Server.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Server as ModelsServer;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Server extends Controller
|
||||
{
|
||||
public function servers(Request $request)
|
||||
{
|
||||
$teamId = get_team_id_from_token();
|
||||
if (is_null($teamId)) {
|
||||
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
|
||||
}
|
||||
$servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) {
|
||||
$server['is_reachable'] = $server->settings->is_reachable;
|
||||
$server['is_usable'] = $server->settings->is_usable;
|
||||
return $server;
|
||||
});
|
||||
ray($servers);
|
||||
return response()->json($servers);
|
||||
}
|
||||
public function server_by_uuid(Request $request)
|
||||
{
|
||||
$teamId = get_team_id_from_token();
|
||||
if (is_null($teamId)) {
|
||||
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
|
||||
}
|
||||
$server = ModelsServer::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
|
||||
if (is_null($server)) {
|
||||
return response()->json(['error' => 'Server not found.'], 404);
|
||||
}
|
||||
$server->load(['settings']);
|
||||
$server['resources'] = $server->definedResources()->map(function ($resource) {
|
||||
$payload = [
|
||||
'id' => $resource->id,
|
||||
'uuid' => $resource->uuid,
|
||||
'name' => $resource->name,
|
||||
'type' => $resource->type(),
|
||||
'created_at' => $resource->created_at,
|
||||
'updated_at' => $resource->updated_at,
|
||||
];
|
||||
if ($resource->type() === 'service') {
|
||||
$payload['status'] = $resource->status();
|
||||
} else {
|
||||
$payload['status'] = $resource->status;
|
||||
}
|
||||
return $payload;
|
||||
});
|
||||
return response()->json($server);
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private Server $mainServer;
|
||||
private ?ApplicationPreview $preview = null;
|
||||
private ?string $git_type = null;
|
||||
private bool $only_this_server = false;
|
||||
|
||||
private string $container_name;
|
||||
private ?string $currently_running_container_name = null;
|
||||
@@ -115,6 +116,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->commit = $this->application_deployment_queue->commit;
|
||||
$this->force_rebuild = $this->application_deployment_queue->force_rebuild;
|
||||
$this->restart_only = $this->application_deployment_queue->restart_only;
|
||||
$this->only_this_server = $this->application_deployment_queue->only_this_server;
|
||||
|
||||
$this->git_type = data_get($this->application_deployment_queue, 'git_type');
|
||||
|
||||
@@ -133,6 +135,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||
|
||||
$this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
|
||||
ray('New container name: ', $this->container_name);
|
||||
|
||||
savePrivateKeyToFs($this->server);
|
||||
$this->saved_outputs = collect();
|
||||
|
||||
@@ -329,7 +333,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private function deploy_dockerimage_buildpack()
|
||||
{
|
||||
$this->dockerImage = $this->application->docker_registry_image_name;
|
||||
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
||||
if (str($this->application->docker_registry_image_tag)->isEmpty()) {
|
||||
$this->dockerImageTag = 'latest';
|
||||
} else {
|
||||
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
||||
}
|
||||
ray("echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.'");
|
||||
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.");
|
||||
$this->generate_image_names();
|
||||
@@ -618,6 +626,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
} else {
|
||||
$this->dockerImageTag = str($this->commit)->substr(0, 128);
|
||||
if ($this->application->docker_registry_image_tag) {
|
||||
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
||||
}
|
||||
if ($this->application->docker_registry_image_name) {
|
||||
$this->build_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}-build");
|
||||
$this->production_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}");
|
||||
@@ -708,9 +719,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->write_deployment_configurations();
|
||||
$this->server = $this->original_server;
|
||||
}
|
||||
if (count($this->application->ports_mappings_array) > 0) {
|
||||
if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled) {
|
||||
$this->application_deployment_queue->addLogEntry("----------------------------------------");
|
||||
$this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
|
||||
if (count($this->application->ports_mappings_array) > 0) {
|
||||
$this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
|
||||
}
|
||||
if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
|
||||
$this->application_deployment_queue->addLogEntry("Consistent container name feature enabled, rolling update is not supported.");
|
||||
}
|
||||
$this->stop_running_container(force: true);
|
||||
$this->start_by_compose_file();
|
||||
} else {
|
||||
@@ -877,7 +893,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
destination: $destination,
|
||||
no_questions_asked: true,
|
||||
);
|
||||
$this->application_deployment_queue->addLogEntry("Deploying to additional server: {$server->name}. Click here to see the deployment status: " . route('project.application.deployment.show', [
|
||||
$this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: " . route('project.application.deployment.show', [
|
||||
'project_uuid' => data_get($this->application, 'environment.project.uuid'),
|
||||
'application_uuid' => data_get($this->application, 'uuid'),
|
||||
'deployment_uuid' => $deployment_uuid,
|
||||
@@ -1012,11 +1028,15 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->env_nixpacks_args = collect([]);
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->nixpacks_environment_variables as $env) {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
|
||||
if (!is_null($env->real_value)) {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
|
||||
if (!is_null($env->real_value)) {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1027,11 +1047,15 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->env_args = collect([]);
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->build_environment_variables as $env) {
|
||||
$this->env_args->put($env->key, $env->real_value);
|
||||
if (!is_null($env->real_value)) {
|
||||
$this->env_args->put($env->key, $env->real_value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->build_environment_variables_preview as $env) {
|
||||
$this->env_args->put($env->key, $env->real_value);
|
||||
if (!is_null($env->real_value)) {
|
||||
$this->env_args->put($env->key, $env->real_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->env_args->put('SOURCE_COMMIT', $this->commit);
|
||||
@@ -1196,13 +1220,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
// ];
|
||||
// }
|
||||
|
||||
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
||||
|
||||
data_forget($docker_compose, 'services.' . $this->container_name);
|
||||
|
||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||
if (count($custom_compose) > 0) {
|
||||
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
||||
if ((bool)$this->application->settings->is_consistent_container_name_enabled) {
|
||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||
if (count($custom_compose) > 0) {
|
||||
$docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
|
||||
}
|
||||
} else {
|
||||
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
||||
data_forget($docker_compose, 'services.' . $this->container_name);
|
||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||
if (count($custom_compose) > 0) {
|
||||
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
||||
}
|
||||
}
|
||||
|
||||
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
||||
@@ -1487,6 +1516,11 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
[executeInDocker($this->deployment_uuid, "docker rm -f $containerName >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||
);
|
||||
});
|
||||
if ($this->application->settings->is_consistent_container_name_enabled) {
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
|
||||
$this->application_deployment_queue->update([
|
||||
@@ -1591,7 +1625,9 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
return;
|
||||
}
|
||||
if ($status === ApplicationDeploymentStatus::FINISHED->value) {
|
||||
$this->deploy_to_additional_destinations();
|
||||
if (!$this->only_this_server) {
|
||||
$this->deploy_to_additional_destinations();
|
||||
}
|
||||
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,17 +44,19 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
public function handle()
|
||||
{
|
||||
$applications = $this->server->applications();
|
||||
$skip_these_applications = collect([]);
|
||||
foreach ($applications as $application) {
|
||||
if ($application->additional_servers->count() > 0) {
|
||||
$is_main_server = $application->destination->server->id === $this->server->id;
|
||||
if ($is_main_server) {
|
||||
ComplexStatusCheck::run($application);
|
||||
$applications = $applications->filter(function ($value, $key) use ($application) {
|
||||
return $value->id !== $application->id;
|
||||
});
|
||||
}
|
||||
$skip_these_applications->push($application);
|
||||
ComplexStatusCheck::run($application);
|
||||
$applications = $applications->filter(function ($value, $key) use ($application) {
|
||||
return $value->id !== $application->id;
|
||||
});
|
||||
}
|
||||
}
|
||||
$applications = $applications->filter(function ($value, $key) use ($skip_these_applications) {
|
||||
return !$skip_these_applications->pluck('id')->contains($value->id);
|
||||
});
|
||||
|
||||
if (!$this->server->isFunctional()) {
|
||||
return 'Server is not ready.';
|
||||
|
||||
59
app/Jobs/GithubAppPermissionJob.php
Normal file
59
app/Jobs/GithubAppPermissionJob.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\GithubApp;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class GithubAppPermissionJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $tries = 4;
|
||||
public function backoff(): int
|
||||
{
|
||||
return isDev() ? 1 : 3;
|
||||
}
|
||||
public function __construct(public GithubApp $github_app)
|
||||
{
|
||||
}
|
||||
public function middleware(): array
|
||||
{
|
||||
return [(new WithoutOverlapping($this->github_app->uuid))];
|
||||
}
|
||||
|
||||
public function uniqueId(): int
|
||||
{
|
||||
return $this->github_app->uuid;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$github_access_token = generate_github_jwt_token($this->github_app);
|
||||
$response = Http::withHeaders([
|
||||
'Authorization' => "Bearer $github_access_token",
|
||||
'Accept' => 'application/vnd.github+json'
|
||||
])->get("{$this->github_app->api_url}/app");
|
||||
$response = $response->json();
|
||||
$permissions = data_get($response, 'permissions');
|
||||
$this->github_app->contents = data_get($permissions, 'contents');
|
||||
$this->github_app->metadata = data_get($permissions, 'metadata');
|
||||
$this->github_app->pull_requests = data_get($permissions, 'pull_requests');
|
||||
$this->github_app->administration = data_get($permissions, 'administration');
|
||||
$this->github_app->save();
|
||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('GithubAppPermissionJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public ?int $disk_usage = null;
|
||||
public int|string|null $disk_usage = null;
|
||||
public $tries = 4;
|
||||
public function backoff(): int
|
||||
{
|
||||
|
||||
@@ -11,6 +11,9 @@ class Index extends Component
|
||||
public $users = [];
|
||||
public function mount()
|
||||
{
|
||||
if (!isCloud()) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
if (auth()->user()->id !== 0 && session('adminToken') === null) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire\Boarding;
|
||||
|
||||
use App\Actions\Server\InstallDocker;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
@@ -121,15 +122,16 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
}
|
||||
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
|
||||
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
||||
$this->validateServer();
|
||||
$this->installServer();
|
||||
}
|
||||
public function getProxyType()
|
||||
{
|
||||
$proxyTypeSet = $this->createdServer->proxy->type;
|
||||
if (!$proxyTypeSet) {
|
||||
$this->currentState = 'select-proxy';
|
||||
return;
|
||||
}
|
||||
$this->selectProxy(ProxyTypes::TRAEFIK_V2->value);
|
||||
// $proxyTypeSet = $this->createdServer->proxy->type;
|
||||
// if (!$proxyTypeSet) {
|
||||
// $this->currentState = 'select-proxy';
|
||||
// return;
|
||||
// }
|
||||
$this->getProjects();
|
||||
}
|
||||
public function selectExistingPrivateKey()
|
||||
@@ -193,7 +195,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
|
||||
$this->createdServer->settings->save();
|
||||
$this->createdServer->addInitialNetwork();
|
||||
$this->validateServer();
|
||||
$this->currentState = 'validate-server';
|
||||
}
|
||||
public function installServer()
|
||||
{
|
||||
@@ -204,7 +206,8 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
try {
|
||||
config()->set('coolify.mux_enabled', false);
|
||||
|
||||
instant_remote_process(['uptime'], $this->createdServer, true);
|
||||
// EC2 does not have `uptime` command, lol
|
||||
instant_remote_process(['ls /'], $this->createdServer, true);
|
||||
|
||||
$this->createdServer->settings()->update([
|
||||
'is_reachable' => true,
|
||||
@@ -219,7 +222,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true);
|
||||
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||
if (is_null($dockerVersion)) {
|
||||
$this->currentState = 'install-docker';
|
||||
$this->currentState = 'validate-server';
|
||||
throw new \Exception('Docker not found or old version is installed.');
|
||||
}
|
||||
$this->createdServer->settings()->update([
|
||||
@@ -227,27 +230,10 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
]);
|
||||
$this->getProxyType();
|
||||
} catch (\Throwable $e) {
|
||||
// $this->dockerInstallationStarted = false;
|
||||
return handleError(error: $e, livewire: $this);
|
||||
}
|
||||
}
|
||||
public function installDocker()
|
||||
{
|
||||
try {
|
||||
$this->dockerInstallationStarted = true;
|
||||
$activity = InstallDocker::run($this->createdServer);
|
||||
$this->dispatch('installDocker');
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
} catch (\Throwable $e) {
|
||||
$this->dockerInstallationStarted = false;
|
||||
return handleError(error: $e, livewire: $this);
|
||||
}
|
||||
}
|
||||
public function dockerInstalledOrSkipped()
|
||||
{
|
||||
$this->validateServer();
|
||||
}
|
||||
public function selectProxy(string|null $proxyType = null)
|
||||
public function selectProxy(?string $proxyType = null)
|
||||
{
|
||||
if (!$proxyType) {
|
||||
return $this->getProjects();
|
||||
|
||||
@@ -22,6 +22,7 @@ class Dashboard extends Component
|
||||
}
|
||||
public function cleanup_queue()
|
||||
{
|
||||
$this->dispatch('success', 'Cleanup started.');
|
||||
Artisan::queue('app:init', [
|
||||
'--cleanup-deployments' => 'true'
|
||||
]);
|
||||
|
||||
@@ -63,7 +63,7 @@ class EmailSettings extends Component
|
||||
]);
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved successfully.');
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ class EmailSettings extends Component
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->team?->notify(new Test($this->emails));
|
||||
$this->dispatch('success', 'Test Email sent successfully.');
|
||||
$this->dispatch('success', 'Test Email sent.');
|
||||
}
|
||||
public function instantSaveInstance()
|
||||
{
|
||||
@@ -83,7 +83,7 @@ class EmailSettings extends Component
|
||||
$this->team->resend_enabled = false;
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved successfully.');
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -131,7 +131,7 @@ class EmailSettings extends Component
|
||||
]);
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved successfully.');
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return handleError($e, $this);
|
||||
@@ -148,7 +148,7 @@ class EmailSettings extends Component
|
||||
]);
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved successfully.');
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->resend_enabled = false;
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -27,7 +27,7 @@ class Index extends Component
|
||||
'name' => $this->name,
|
||||
]);
|
||||
|
||||
$this->dispatch('success', 'Profile updated successfully.');
|
||||
$this->dispatch('success', 'Profile updated');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -8,20 +8,25 @@ use Livewire\Component;
|
||||
class Advanced extends Component
|
||||
{
|
||||
public Application $application;
|
||||
public bool $is_force_https_enabled;
|
||||
protected $rules = [
|
||||
'application.settings.is_git_submodules_enabled' => 'boolean|required',
|
||||
'application.settings.is_git_lfs_enabled' => 'boolean|required',
|
||||
'application.settings.is_preview_deployments_enabled' => 'boolean|required',
|
||||
'application.settings.is_auto_deploy_enabled' => 'boolean|required',
|
||||
'application.settings.is_force_https_enabled' => 'boolean|required',
|
||||
'is_force_https_enabled' => 'boolean|required',
|
||||
'application.settings.is_log_drain_enabled' => 'boolean|required',
|
||||
'application.settings.is_gpu_enabled' => 'boolean|required',
|
||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||
'application.settings.is_consistent_container_name_enabled' => 'boolean|required',
|
||||
'application.settings.gpu_driver' => 'string|required',
|
||||
'application.settings.gpu_count' => 'string|required',
|
||||
'application.settings.gpu_device_ids' => 'string|required',
|
||||
'application.settings.gpu_options' => 'string|required',
|
||||
];
|
||||
public function mount() {
|
||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
if ($this->application->isLogDrainEnabled()) {
|
||||
@@ -31,7 +36,8 @@ class Advanced extends Component
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ($this->application->settings->is_force_https_enabled) {
|
||||
if ($this->application->settings->is_force_https_enabled !== $this->is_force_https_enabled) {
|
||||
$this->application->settings->is_force_https_enabled = $this->is_force_https_enabled;
|
||||
$this->dispatch('resetDefaultLabels', false);
|
||||
}
|
||||
$this->application->settings->save();
|
||||
|
||||
@@ -126,7 +126,6 @@ class General extends Component
|
||||
$this->application->save();
|
||||
}
|
||||
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||
$this->checkLabelUpdates();
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
@@ -164,6 +163,7 @@ class General extends Component
|
||||
}
|
||||
return $domain;
|
||||
}
|
||||
|
||||
public function updatedApplicationBuildPack()
|
||||
{
|
||||
if ($this->application->build_pack !== 'nixpacks') {
|
||||
@@ -184,15 +184,6 @@ class General extends Component
|
||||
$this->submit();
|
||||
$this->dispatch('build_pack_updated');
|
||||
}
|
||||
public function checkLabelUpdates()
|
||||
{
|
||||
if (md5($this->application->custom_labels) !== md5(implode("|", generateLabelsApplication($this->application)))) {
|
||||
$this->labelsChanged = true;
|
||||
} else {
|
||||
$this->labelsChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getWildcardDomain()
|
||||
{
|
||||
$server = data_get($this->application, 'destination.server');
|
||||
@@ -212,6 +203,13 @@ class General extends Component
|
||||
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->application->save();
|
||||
$this->resetDefaultLabels(false);
|
||||
// $this->dispatch('success', 'Labels reset to default!');
|
||||
}
|
||||
@@ -234,26 +232,20 @@ class General extends Component
|
||||
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||
$this->validate([
|
||||
'application.docker_registry_image_name' => 'required',
|
||||
'application.docker_registry_image_tag' => 'required',
|
||||
]);
|
||||
}
|
||||
if (data_get($this->application, 'fqdn')) {
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$domains = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$domains = $domains->unique();
|
||||
$domains = str($this->application->fqdn)->trim()->explode(',');
|
||||
if ($this->application->additional_servers->count() === 0) {
|
||||
foreach ($domains as $domain) {
|
||||
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
||||
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.","Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='text-white underline' href='https://coolify.io/docs/dns-settings'>documentation</a> for further help.");
|
||||
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='text-white underline' href='https://coolify.io/docs/dns-settings'>documentation</a> for further help.");
|
||||
}
|
||||
}
|
||||
}
|
||||
check_fqdn_usage($this->application);
|
||||
$this->application->fqdn = $domains->implode(',');
|
||||
}
|
||||
|
||||
if (data_get($this->application, 'custom_docker_run_options')) {
|
||||
$this->application->custom_docker_run_options = str($this->application->custom_docker_run_options)->trim();
|
||||
}
|
||||
@@ -279,7 +271,6 @@ class General extends Component
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->checkLabelUpdates();
|
||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,15 +33,11 @@ class Heading extends Component
|
||||
{
|
||||
if ($this->application->destination->server->isFunctional()) {
|
||||
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||
// $this->application->refresh();
|
||||
// $this->application->previews->each(function ($preview) {
|
||||
// $preview->refresh();
|
||||
// });
|
||||
} else {
|
||||
dispatch(new ServerStatusJob($this->application->destination->server));
|
||||
}
|
||||
|
||||
if ($showNotification) $this->dispatch('success', "Application status updated.");
|
||||
if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
|
||||
}
|
||||
|
||||
public function force_deploy_without_cache()
|
||||
@@ -56,15 +52,15 @@ class Heading extends Component
|
||||
return;
|
||||
}
|
||||
if ($this->application->destination->server->isSwarm() && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy', 'To deploy to a Swarm cluster you must set a Docker image name first.');
|
||||
$this->dispatch('error', 'Failed to deploy.', 'To deploy to a Swarm cluster you must set a Docker image name first.');
|
||||
return;
|
||||
}
|
||||
if (data_get($this->application, 'settings.is_build_server_enabled') && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
|
||||
return;
|
||||
}
|
||||
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy', 'To deploy to more than one server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
|
||||
return;
|
||||
}
|
||||
$this->setDeploymentUuid();
|
||||
@@ -103,7 +99,7 @@ class Heading extends Component
|
||||
public function restart()
|
||||
{
|
||||
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy', 'To deploy to more than one server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
|
||||
return;
|
||||
}
|
||||
$this->setDeploymentUuid();
|
||||
|
||||
@@ -46,7 +46,7 @@ class Form extends Component
|
||||
$this->validate();
|
||||
$this->application->preview_url_template = str_replace(' ', '', $this->application->preview_url_template);
|
||||
$this->application->save();
|
||||
$this->dispatch('success', 'Preview url template updated successfully.');
|
||||
$this->dispatch('success', 'Preview url template updated.');
|
||||
$this->generate_real_url();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class BackupExecutions extends Component
|
||||
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
|
||||
}
|
||||
$execution->delete();
|
||||
$this->dispatch('success', 'Backup deleted successfully.');
|
||||
$this->dispatch('success', 'Backup deleted.');
|
||||
$this->dispatch('refreshBackupExecutions');
|
||||
}
|
||||
public function download($exeuctionId)
|
||||
|
||||
@@ -59,7 +59,7 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -73,7 +73,7 @@ class General extends Component
|
||||
}
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -75,7 +75,7 @@ class General extends Component
|
||||
}
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -74,7 +74,7 @@ class General extends Component
|
||||
}
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -105,7 +105,7 @@ class General extends Component
|
||||
$this->database->init_scripts = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']);
|
||||
$this->database->init_scripts = array_merge($this->database->init_scripts, [$script]);
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Init script saved successfully.');
|
||||
$this->dispatch('success', 'Init script saved.');
|
||||
}
|
||||
|
||||
public function delete_init_script($script)
|
||||
@@ -116,7 +116,7 @@ class General extends Component
|
||||
$this->database->init_scripts = $collection->filter(fn ($s) => $s['filename'] !== $script['filename'])->toArray();
|
||||
$this->database->save();
|
||||
$this->refresh();
|
||||
$this->dispatch('success', 'Init script deleted successfully.');
|
||||
$this->dispatch('success', 'Init script deleted.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@ class General extends Component
|
||||
]
|
||||
]);
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Init script added successfully.');
|
||||
$this->dispatch('success', 'Init script added.');
|
||||
$this->new_content = '';
|
||||
$this->new_filename = '';
|
||||
}
|
||||
@@ -161,7 +161,7 @@ class General extends Component
|
||||
}
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -66,7 +66,7 @@ class General extends Component
|
||||
$this->database->redis_conf = null;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->dispatch('success', 'Database updated successfully.');
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class ScheduledBackups extends Component
|
||||
public function delete($scheduled_backup_id): void
|
||||
{
|
||||
$this->database->scheduledBackups->find($scheduled_backup_id)->delete();
|
||||
$this->dispatch('success', 'Scheduled backup deleted successfully.');
|
||||
$this->dispatch('success', 'Scheduled backup deleted.');
|
||||
$this->refreshScheduledBackups();
|
||||
}
|
||||
|
||||
|
||||
@@ -71,10 +71,10 @@ class Select extends Component
|
||||
// }
|
||||
// }
|
||||
|
||||
public function loadServices()
|
||||
public function loadServices(bool $force = false)
|
||||
{
|
||||
try {
|
||||
if (count($this->allServices) > 0) {
|
||||
if (count($this->allServices) > 0 && !$force) {
|
||||
if (!$this->search) {
|
||||
$this->services = $this->allServices;
|
||||
return;
|
||||
|
||||
@@ -104,7 +104,7 @@ class Index extends Component
|
||||
'environment_name' => data_get($service, 'environment.name'),
|
||||
'service_uuid' => data_get($service, 'uuid')
|
||||
]);
|
||||
$service->status = serviceStatus($service);
|
||||
$service->status = $service->status();
|
||||
}
|
||||
return $service;
|
||||
});
|
||||
|
||||
@@ -77,7 +77,7 @@ class Database extends Component
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
updateCompose($this->database);
|
||||
$this->dispatch('success', 'Database saved successfully.');
|
||||
$this->dispatch('success', 'Database saved.');
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
} finally {
|
||||
|
||||
@@ -41,8 +41,8 @@ class FileStorage extends Component
|
||||
$this->fileStorage->content = null;
|
||||
}
|
||||
$this->fileStorage->save();
|
||||
$this->fileStorage->saveStorageOnServer($this->service);
|
||||
$this->dispatch('success', 'File updated successfully.');
|
||||
$this->fileStorage->saveStorageOnServer();
|
||||
$this->dispatch('success', 'File updated.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->fileStorage->setRawAttributes($original);
|
||||
$this->fileStorage->save();
|
||||
|
||||
@@ -27,12 +27,12 @@ class Index extends Component
|
||||
$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();
|
||||
$service = $this->service->applications()->whereUuid($this->parameters['stack_service_uuid'])->first();
|
||||
if ($service) {
|
||||
$this->serviceApplication = $service;
|
||||
$this->serviceApplication->getFilesFromServer();
|
||||
} else {
|
||||
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
||||
$this->serviceDatabase = $this->service->databases()->whereUuid($this->parameters['stack_service_uuid'])->first();
|
||||
$this->serviceDatabase->getFilesFromServer();
|
||||
}
|
||||
$this->s3s = currentTeam()->s3s;
|
||||
|
||||
@@ -64,9 +64,9 @@ class Navbar extends Component
|
||||
StopService::run($this->service);
|
||||
$this->service->refresh();
|
||||
if ($forceCleanup) {
|
||||
$this->dispatch('success', 'Force cleanup service successfully.');
|
||||
$this->dispatch('success', 'Force cleanup service.');
|
||||
} else {
|
||||
$this->dispatch('success', 'Service stopped successfully.');
|
||||
$this->dispatch('success', 'Service stopped.');
|
||||
}
|
||||
ServiceStatusChanged::dispatch();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ class ServiceApplicationView extends Component
|
||||
'application.exclude_from_status' => 'required|boolean',
|
||||
'application.required_fqdn' => 'required|boolean',
|
||||
'application.is_log_drain_enabled' => 'nullable|boolean',
|
||||
'application.is_gzip_enabled' => 'nullable|boolean',
|
||||
];
|
||||
public function render()
|
||||
{
|
||||
@@ -40,7 +41,7 @@ class ServiceApplicationView extends Component
|
||||
{
|
||||
try {
|
||||
$this->application->delete();
|
||||
$this->dispatch('success', 'Application deleted successfully.');
|
||||
$this->dispatch('success', 'Application deleted.');
|
||||
return redirect()->route('project.service.configuration', $this->parameters);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -57,7 +58,7 @@ class ServiceApplicationView extends Component
|
||||
$this->validate();
|
||||
$this->application->save();
|
||||
updateCompose($this->application);
|
||||
$this->dispatch('success', 'Application saved successfully.');
|
||||
$this->dispatch('success', 'Application saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
|
||||
@@ -45,8 +45,10 @@ class StackForm extends Component
|
||||
$this->service->docker_compose_raw = $raw;
|
||||
$this->submit();
|
||||
}
|
||||
public function instantSave() {
|
||||
public function instantSave()
|
||||
{
|
||||
$this->service->save();
|
||||
$this->dispatch('success', 'Service settings saved.');
|
||||
}
|
||||
|
||||
public function submit()
|
||||
@@ -60,7 +62,7 @@ class StackForm extends Component
|
||||
$this->service->saveComposeConfigs();
|
||||
$this->dispatch('refreshStacks');
|
||||
$this->dispatch('refreshEnvs');
|
||||
$this->dispatch('success', 'Service saved successfully.');
|
||||
$this->dispatch('success', 'Service saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Shared;
|
||||
|
||||
use App\Actions\Application\StopApplicationOneServer;
|
||||
use App\Events\ApplicationStatusChanged;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use Livewire\Component;
|
||||
@@ -37,10 +38,27 @@ class Destination extends Component
|
||||
$this->networks = $this->networks->reject(function ($network) use ($all_networks) {
|
||||
return $all_networks->pluck('id')->contains($network->id);
|
||||
});
|
||||
|
||||
$this->networks = $this->networks->reject(function ($network) {
|
||||
return $this->resource->destination->server->id == $network->server->id;
|
||||
});
|
||||
if ($this->resource?->additional_servers?->count() > 0) {
|
||||
$this->networks = $this->networks->reject(function ($network) {
|
||||
return $this->resource->additional_servers->pluck('id')->contains($network->server->id);
|
||||
});
|
||||
}
|
||||
}
|
||||
public function stop(int $server_id)
|
||||
{
|
||||
$server = Server::find($server_id);
|
||||
StopApplicationOneServer::run($this->resource, $server);
|
||||
$this->refreshServers();
|
||||
}
|
||||
public function redeploy(int $network_id, int $server_id)
|
||||
{
|
||||
if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
|
||||
return;
|
||||
}
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
$server = Server::find($server_id);
|
||||
$destination = StandaloneDocker::find($network_id);
|
||||
@@ -49,6 +67,7 @@ class Destination extends Component
|
||||
application: $this->resource,
|
||||
server: $server,
|
||||
destination: $destination,
|
||||
only_this_server: true,
|
||||
no_questions_asked: true,
|
||||
);
|
||||
return redirect()->route('project.application.deployment.show', [
|
||||
@@ -58,24 +77,40 @@ class Destination extends Component
|
||||
'environment_name' => data_get($this->resource, 'environment.name'),
|
||||
]);
|
||||
}
|
||||
public function promote(int $network_id, int $server_id)
|
||||
{
|
||||
$main_destination = $this->resource->destination;
|
||||
$this->resource->update([
|
||||
'destination_id' => $network_id,
|
||||
'destination_type' => StandaloneDocker::class,
|
||||
]);
|
||||
$this->resource->additional_networks()->detach($network_id, ['server_id' => $server_id]);
|
||||
$this->resource->additional_networks()->attach($main_destination->id, ['server_id' => $main_destination->server->id]);
|
||||
$this->refreshServers();
|
||||
}
|
||||
public function refreshServers()
|
||||
{
|
||||
ContainerStatusJob::dispatchSync($this->resource->destination->server);
|
||||
$this->loadData();
|
||||
$this->dispatch('refresh');
|
||||
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||
}
|
||||
public function addServer(int $network_id, int $server_id)
|
||||
{
|
||||
$this->resource->additional_networks()->attach($network_id, ['server_id' => $server_id]);
|
||||
$this->resource->load(['additional_networks']);
|
||||
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||
$this->loadData();
|
||||
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||
}
|
||||
public function removeServer(int $network_id, int $server_id)
|
||||
{
|
||||
if ($this->resource->destination->server->id == $server_id) {
|
||||
if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) {
|
||||
$this->dispatch('error', 'You cannot remove this destination server.', 'You are trying to remove the main server.');
|
||||
return;
|
||||
}
|
||||
$server = Server::find($server_id);
|
||||
StopApplicationOneServer::run($this->resource, $server);
|
||||
$this->resource->additional_networks()->detach($network_id, ['server_id' => $server_id]);
|
||||
$this->resource->load(['additional_networks']);
|
||||
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||
$this->loadData();
|
||||
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,9 +120,9 @@ class All extends Component
|
||||
}
|
||||
}
|
||||
if ($isPreview) {
|
||||
$this->dispatch('success', 'Preview environment variables updated successfully.');
|
||||
$this->dispatch('success', 'Preview environment variables updated.');
|
||||
} else {
|
||||
$this->dispatch('success', 'Environment variables updated successfully.');
|
||||
$this->dispatch('success', 'Environment variables updated.');
|
||||
}
|
||||
$this->refreshEnvs();
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ class Show extends Component
|
||||
}
|
||||
$this->serialize();
|
||||
$this->env->save();
|
||||
$this->dispatch('success', 'Environment variable updated successfully.');
|
||||
$this->dispatch('success', 'Environment variable updated.');
|
||||
$this->dispatch('refreshEnvs');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
|
||||
@@ -52,7 +52,7 @@ class ResourceLimits extends Component
|
||||
}
|
||||
$this->validate();
|
||||
$this->resource->save();
|
||||
$this->dispatch('success', 'Resource limits updated successfully.');
|
||||
$this->dispatch('success', 'Resource limits updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class All extends Component
|
||||
}
|
||||
$task->save();
|
||||
$this->refreshTasks();
|
||||
$this->dispatch('success', 'Scheduled task added successfully.');
|
||||
$this->dispatch('success', 'Scheduled task added.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class Show extends Component
|
||||
{
|
||||
$this->validate();
|
||||
$this->task->save();
|
||||
$this->dispatch('success', 'Scheduled task updated successfully.');
|
||||
$this->dispatch('success', 'Scheduled task updated.');
|
||||
$this->dispatch('refreshTasks');
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,9 @@ class Form extends Component
|
||||
public ?string $wildcard_domain = null;
|
||||
public int $cleanup_after_percentage;
|
||||
public bool $dockerInstallationStarted = false;
|
||||
public bool $revalidate = false;
|
||||
|
||||
protected $listeners = ['serverInstalled'];
|
||||
protected $listeners = ['serverInstalled', 'revalidate' => '$refresh'];
|
||||
|
||||
protected $rules = [
|
||||
'server.name' => 'required',
|
||||
@@ -63,11 +64,15 @@ class Form extends Component
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->validateServer(false);
|
||||
$this->server->settings->save();
|
||||
$this->dispatch('success', 'Server updated successfully.');
|
||||
$this->dispatch('success', 'Server updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function revalidate()
|
||||
{
|
||||
$this->revalidate = true;
|
||||
}
|
||||
public function checkLocalhostConnection()
|
||||
{
|
||||
$uptime = $this->server->validateConnection();
|
||||
@@ -83,7 +88,7 @@ class Form extends Component
|
||||
}
|
||||
public function validateServer($install = true)
|
||||
{
|
||||
$this->dispatch('validateServer', $install);
|
||||
$this->dispatch('init', $install);
|
||||
}
|
||||
|
||||
public function submit()
|
||||
@@ -108,6 +113,6 @@ class Form extends Component
|
||||
$this->server->settings->cleanup_after_percentage = $this->cleanup_after_percentage;
|
||||
$this->server->settings->save();
|
||||
$this->server->save();
|
||||
$this->dispatch('success', 'Server updated successfully.');
|
||||
$this->dispatch('success', 'Server updated.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class LogDrains extends Component
|
||||
return;
|
||||
}
|
||||
$this->dispatch('serverRefresh');
|
||||
$this->dispatch('success', 'Log drain service started successfully.');
|
||||
$this->dispatch('success', 'Log drain service started.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -126,7 +126,7 @@ class LogDrains extends Component
|
||||
]);
|
||||
}
|
||||
$this->server->settings->save();
|
||||
$this->dispatch('success', 'Settings saved successfully.');
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
if ($type === 'newrelic') {
|
||||
|
||||
28
app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
Normal file
28
app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server\Proxy;
|
||||
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
class DynamicConfigurationNavbar extends Component
|
||||
{
|
||||
public $server_id;
|
||||
public $fileName = '';
|
||||
public $value = '';
|
||||
public $newFile = false;
|
||||
public function delete(string $fileName)
|
||||
{
|
||||
$server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first();
|
||||
$proxy_path = get_proxy_path();
|
||||
$file = str_replace('|', '.', $fileName);
|
||||
instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server);
|
||||
$this->dispatch('success', 'File deleted.');
|
||||
$this->dispatch('loadDynamicConfigurations');
|
||||
$this->dispatch('refresh');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.proxy.dynamic-configuration-navbar');
|
||||
}
|
||||
}
|
||||
51
app/Livewire/Server/Proxy/DynamicConfigurations.php
Normal file
51
app/Livewire/Server/Proxy/DynamicConfigurations.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server\Proxy;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class DynamicConfigurations extends Component
|
||||
{
|
||||
public ?Server $server = null;
|
||||
public $parameters = [];
|
||||
public Collection $contents;
|
||||
protected $listeners = ['loadDynamicConfigurations', 'refresh' => '$refresh'];
|
||||
protected $rules = [
|
||||
'contents.*' => 'nullable|string',
|
||||
];
|
||||
public function loadDynamicConfigurations()
|
||||
{
|
||||
$proxy_path = get_proxy_path();
|
||||
$files = instant_remote_process(["mkdir -p $proxy_path/dynamic && ls -1 {$proxy_path}/dynamic"], $this->server);
|
||||
$files = collect(explode("\n", $files))->filter(fn ($file) => !empty($file));
|
||||
$files = $files->map(fn ($file) => trim($file));
|
||||
$files = $files->sort();
|
||||
if ($files->contains('coolify.yaml')) {
|
||||
$files = $files->filter(fn ($file) => $file !== 'coolify.yaml')->prepend('coolify.yaml');
|
||||
}
|
||||
$contents = collect([]);
|
||||
foreach ($files as $file) {
|
||||
$without_extension = str_replace('.', '|', $file);
|
||||
$contents[$without_extension] = instant_remote_process(["cat {$proxy_path}/dynamic/{$file}"], $this->server);
|
||||
}
|
||||
$this->contents = $contents;
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
try {
|
||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||
if (is_null($this->server)) {
|
||||
return redirect()->route('server.index');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.proxy.dynamic-configurations');
|
||||
}
|
||||
}
|
||||
74
app/Livewire/Server/Proxy/NewDynamicConfiguration.php
Normal file
74
app/Livewire/Server/Proxy/NewDynamicConfiguration.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server\Proxy;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Routing\Route;
|
||||
use Livewire\Component;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class NewDynamicConfiguration extends Component
|
||||
{
|
||||
public string $fileName = '';
|
||||
public string $value = '';
|
||||
public bool $newFile = false;
|
||||
public Server $server;
|
||||
public $server_id;
|
||||
public $parameters = [];
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
if ($this->fileName !== '') {
|
||||
$this->fileName = str_replace('|', '.', $this->fileName);
|
||||
}
|
||||
}
|
||||
public function addDynamicConfiguration()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'fileName' => 'required',
|
||||
'value' => 'required',
|
||||
]);
|
||||
|
||||
if (data_get($this->parameters, 'server_uuid')) {
|
||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(data_get($this->parameters, 'server_uuid'))->first();
|
||||
}
|
||||
if (!is_null($this->server_id)) {
|
||||
$this->server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first();
|
||||
}
|
||||
if (is_null($this->server)) {
|
||||
return redirect()->route('server.index');
|
||||
}
|
||||
if (!str($this->fileName)->endsWith('.yaml') && !str($this->fileName)->endsWith('.yml')) {
|
||||
$this->fileName = "{$this->fileName}.yaml";
|
||||
}
|
||||
if ($this->fileName === 'coolify.yaml') {
|
||||
$this->dispatch('error', 'File name is reserved.');
|
||||
return;
|
||||
}
|
||||
$proxy_path = get_proxy_path();
|
||||
$file = "{$proxy_path}/dynamic/{$this->fileName}";
|
||||
if ($this->newFile) {
|
||||
$exists = instant_remote_process(["test -f $file && echo 1 || echo 0"], $this->server);
|
||||
if ($exists == 1) {
|
||||
$this->dispatch('error', 'File already exists');
|
||||
return;
|
||||
}
|
||||
}
|
||||
$yaml = Yaml::parse($this->value);
|
||||
$yaml = Yaml::dump($yaml, 10, 2);
|
||||
$this->value = $yaml;
|
||||
$base64_value = base64_encode($this->value);
|
||||
instant_remote_process(["echo '{$base64_value}' | base64 -d > {$file}"], $this->server);
|
||||
$this->dispatch('loadDynamicConfigurations');
|
||||
$this->dispatch('dynamic-configuration-added');
|
||||
$this->dispatch('success', 'Dynamic configuration saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.proxy.new-dynamic-configuration');
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ class Status extends Component
|
||||
public Server $server;
|
||||
public bool $polling = false;
|
||||
public int $numberOfPolls = 0;
|
||||
protected $listeners = ['proxyStatusUpdated', 'startProxyPolling'];
|
||||
protected $listeners = ['proxyStatusUpdated' => '$refresh', 'startProxyPolling'];
|
||||
|
||||
public function startProxyPolling()
|
||||
{
|
||||
|
||||
64
app/Livewire/Server/Resources.php
Normal file
64
app/Livewire/Server/Resources.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class Resources extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
public ?Server $server = null;
|
||||
public $parameters = [];
|
||||
public Collection $unmanagedContainers;
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = auth()->user()->currentTeam()->id;
|
||||
return [
|
||||
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'refreshStatus',
|
||||
];
|
||||
}
|
||||
|
||||
public function startUnmanaged($id) {
|
||||
$this->server->startUnmanaged($id);
|
||||
$this->dispatch('success', 'Container started.');
|
||||
$this->loadUnmanagedContainers();
|
||||
}
|
||||
public function restartUnmanaged($id) {
|
||||
$this->server->restartUnmanaged($id);
|
||||
$this->dispatch('success', 'Container restarted.');
|
||||
$this->loadUnmanagedContainers();
|
||||
}
|
||||
public function stopUnmanaged($id) {
|
||||
$this->server->stopUnmanaged($id);
|
||||
$this->dispatch('success', 'Container stopped.');
|
||||
$this->loadUnmanagedContainers();
|
||||
}
|
||||
public function refreshStatus() {
|
||||
$this->server->refresh();
|
||||
$this->loadUnmanagedContainers();
|
||||
$this->dispatch('success', 'Resource statuses refreshed.');
|
||||
}
|
||||
public function loadUnmanagedContainers() {
|
||||
$this->unmanagedContainers = $this->server->loadUnmanagedContainers();
|
||||
}
|
||||
public function mount() {
|
||||
$this->unmanagedContainers = collect();
|
||||
$this->parameters = get_route_parameters();
|
||||
try {
|
||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||
if (is_null($this->server)) {
|
||||
return redirect()->route('server.index');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->loadUnmanagedContainers();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.resources');
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -14,10 +16,21 @@ class ValidateAndInstall extends Component
|
||||
public $uptime = null;
|
||||
public $supported_os_type = null;
|
||||
public $docker_installed = null;
|
||||
public $docker_compose_installed = null;
|
||||
public $docker_version = null;
|
||||
public $proxy_started = false;
|
||||
public $error = null;
|
||||
public bool $ask = false;
|
||||
|
||||
protected $listeners = ['validateServer' => 'init', 'validateDockerEngine', 'validateServerNow' => 'validateServer'];
|
||||
protected $listeners = [
|
||||
'init',
|
||||
'validateConnection',
|
||||
'validateOS',
|
||||
'validateDockerEngine',
|
||||
'validateDockerVersion',
|
||||
'startProxy',
|
||||
'refresh' => '$refresh',
|
||||
];
|
||||
|
||||
public function init(bool $install = true)
|
||||
{
|
||||
@@ -26,24 +39,34 @@ class ValidateAndInstall extends Component
|
||||
$this->supported_os_type = null;
|
||||
$this->docker_installed = null;
|
||||
$this->docker_version = null;
|
||||
$this->docker_compose_installed = null;
|
||||
$this->proxy_started = null;
|
||||
$this->error = null;
|
||||
$this->number_of_tries = 0;
|
||||
$this->dispatch('validateServerNow');
|
||||
if (!$this->ask) {
|
||||
$this->dispatch('validateConnection');
|
||||
}
|
||||
}
|
||||
|
||||
public function validateServer()
|
||||
public function startValidatingAfterAsking()
|
||||
{
|
||||
$this->ask = false;
|
||||
$this->init();
|
||||
}
|
||||
public function startProxy()
|
||||
{
|
||||
try {
|
||||
$this->validateConnection();
|
||||
$this->validateOS();
|
||||
$this->validateDockerEngine();
|
||||
|
||||
if ($this->server->isSwarm()) {
|
||||
$swarmInstalled = $this->server->validateDockerSwarm();
|
||||
if ($swarmInstalled) {
|
||||
$this->dispatch('success', 'Docker Swarm is initiated.');
|
||||
$shouldStart = CheckProxy::run($this->server);
|
||||
if ($shouldStart) {
|
||||
$proxy = StartProxy::run($this->server, false);
|
||||
if ($proxy === 'OK') {
|
||||
$this->proxy_started = true;
|
||||
} else {
|
||||
throw new \Exception("Proxy could not be started.");
|
||||
}
|
||||
} else {
|
||||
$this->proxy_started = true;
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -55,6 +78,7 @@ class ValidateAndInstall extends Component
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh">documentation</a> for further help.';
|
||||
return;
|
||||
}
|
||||
$this->dispatch('validateOS');
|
||||
}
|
||||
public function validateOS()
|
||||
{
|
||||
@@ -63,40 +87,51 @@ class ValidateAndInstall extends Component
|
||||
$this->error = 'Server OS type is not supported. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
return;
|
||||
}
|
||||
$this->dispatch('validateDockerEngine');
|
||||
}
|
||||
public function validateDockerEngine()
|
||||
{
|
||||
$this->docker_installed = $this->server->validateDockerEngine();
|
||||
if (!$this->docker_installed) {
|
||||
$this->docker_compose_installed = $this->server->validateDockerCompose();
|
||||
if (!$this->docker_installed || !$this->docker_compose_installed) {
|
||||
if ($this->install) {
|
||||
ray($this->number_of_tries, $this->max_tries);
|
||||
if ($this->number_of_tries == $this->max_tries) {
|
||||
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
return;
|
||||
} else {
|
||||
$activity = $this->server->installDocker();
|
||||
$this->number_of_tries++;
|
||||
$this->dispatch('newActivityMonitor', $activity->id, 'validateDockerEngine');
|
||||
if ($this->number_of_tries == 0) {
|
||||
$activity = $this->server->installDocker();
|
||||
$this->number_of_tries++;
|
||||
$this->dispatch('newActivityMonitor', $activity->id, 'init');
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$this->validateDockerVersion();
|
||||
}
|
||||
$this->dispatch('validateDockerVersion');
|
||||
}
|
||||
public function validateDockerVersion()
|
||||
{
|
||||
$this->docker_version = $this->server->validateDockerEngineVersion();
|
||||
if ($this->docker_version) {
|
||||
$this->dispatch('serverInstalled');
|
||||
$this->dispatch('success', 'Server validated successfully.');
|
||||
if ($this->server->isSwarm()) {
|
||||
$swarmInstalled = $this->server->validateDockerSwarm();
|
||||
if ($swarmInstalled) {
|
||||
$this->dispatch('success', 'Docker Swarm is initiated.');
|
||||
}
|
||||
} else {
|
||||
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
return;
|
||||
$this->docker_version = $this->server->validateDockerEngineVersion();
|
||||
if ($this->docker_version) {
|
||||
$this->dispatch('serverInstalled');
|
||||
$this->dispatch('success', 'Server validated.');
|
||||
} else {
|
||||
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->dispatch('startProxy');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
|
||||
@@ -81,6 +81,6 @@ class Backup extends Component
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
$this->dispatch('success', 'Backup updated successfully.');
|
||||
$this->dispatch('success', 'Backup updated.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class Email extends Component
|
||||
'settings.smtp_from_name' => 'required',
|
||||
]);
|
||||
$this->settings->save();
|
||||
$this->dispatch('success', 'Settings saved successfully.');
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -61,7 +61,7 @@ class Email extends Component
|
||||
'settings.resend_api_key' => 'required'
|
||||
]);
|
||||
$this->settings->save();
|
||||
$this->dispatch('success', 'Settings saved successfully.');
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->settings->resend_enabled = false;
|
||||
return handleError($e, $this);
|
||||
@@ -98,7 +98,7 @@ class Email extends Component
|
||||
'settings.smtp_timeout' => 'nullable',
|
||||
]);
|
||||
$this->settings->save();
|
||||
$this->dispatch('success', 'Settings saved successfully.');
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Livewire\Source\Github;
|
||||
|
||||
use App\Jobs\GithubAppPermissionJob;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Component;
|
||||
|
||||
class Change extends Component
|
||||
@@ -15,6 +17,7 @@ class Change extends Component
|
||||
|
||||
public ?bool $default_permissions = true;
|
||||
public ?bool $preview_deployment_permissions = true;
|
||||
public ?bool $administration = false;
|
||||
|
||||
public $parameters;
|
||||
public ?GithubApp $github_app;
|
||||
@@ -34,8 +37,52 @@ class Change extends Component
|
||||
'github_app.client_secret' => 'required|string',
|
||||
'github_app.webhook_secret' => 'required|string',
|
||||
'github_app.is_system_wide' => 'required|bool',
|
||||
'github_app.contents' => 'nullable|string',
|
||||
'github_app.metadata' => 'nullable|string',
|
||||
'github_app.pull_requests' => 'nullable|string',
|
||||
'github_app.administration' => 'nullable|string',
|
||||
];
|
||||
|
||||
public function checkPermissions()
|
||||
{
|
||||
GithubAppPermissionJob::dispatchSync($this->github_app);
|
||||
$this->github_app->refresh()->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
$this->dispatch('success', 'Github App permissions updated.');
|
||||
}
|
||||
// public function check()
|
||||
// {
|
||||
|
||||
// Need administration:read:write permission
|
||||
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-a-repository
|
||||
|
||||
|
||||
// $github_access_token = generate_github_installation_token($this->github_app);
|
||||
// $repositories = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/installation/repositories?per_page=100");
|
||||
// $runners_by_repository = collect([]);
|
||||
// $repositories = $repositories->json()['repositories'];
|
||||
// foreach ($repositories as $repository) {
|
||||
// $runners_downloads = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/repos/{$repository['full_name']}/actions/runners/downloads");
|
||||
// $runners = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/repos/{$repository['full_name']}/actions/runners");
|
||||
// $token = Http::withHeaders([
|
||||
// 'Authorization' => "Bearer $github_access_token",
|
||||
// 'Accept' => 'application/vnd.github+json'
|
||||
// ])->withBody(null)->post("{$this->github_app->api_url}/repos/{$repository['full_name']}/actions/runners/registration-token");
|
||||
// $token = $token->json();
|
||||
// $remove_token = Http::withHeaders([
|
||||
// 'Authorization' => "Bearer $github_access_token",
|
||||
// 'Accept' => 'application/vnd.github+json'
|
||||
// ])->withBody(null)->post("{$this->github_app->api_url}/repos/{$repository['full_name']}/actions/runners/remove-token");
|
||||
// $remove_token = $remove_token->json();
|
||||
// $runners_by_repository->put($repository['full_name'], [
|
||||
// 'token' => $token,
|
||||
// 'remove_token' => $remove_token,
|
||||
// 'runners' => $runners->json(),
|
||||
// 'runners_downloads' => $runners_downloads->json()
|
||||
// ]);
|
||||
// }
|
||||
|
||||
// ray($runners_by_repository);
|
||||
// }
|
||||
public function mount()
|
||||
{
|
||||
$github_app_uuid = request()->github_app_uuid;
|
||||
@@ -103,7 +150,7 @@ class Change extends Component
|
||||
'github_app.is_system_wide' => 'required|bool',
|
||||
]);
|
||||
$this->github_app->save();
|
||||
$this->dispatch('success', 'Github App updated successfully.');
|
||||
$this->dispatch('success', 'Github App updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -111,6 +158,13 @@ class Change extends Component
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
$this->github_app->save();
|
||||
$this->dispatch('success', 'Github App updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete()
|
||||
|
||||
@@ -37,7 +37,7 @@ class Index extends Component
|
||||
try {
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Team updated successfully.');
|
||||
$this->dispatch('success', 'Team updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class InviteLink extends Component
|
||||
]);
|
||||
$mail->subject('You have been invited to ' . currentTeam()->name . ' on ' . config('app.name') . '.');
|
||||
send_user_an_email($mail, $this->email);
|
||||
$this->dispatch('success', 'Invitation sent via email successfully.');
|
||||
$this->dispatch('success', 'Invitation sent via email.');
|
||||
$this->dispatch('refreshInvitations');
|
||||
return;
|
||||
} else {
|
||||
|
||||
@@ -48,6 +48,9 @@ class Application extends BaseModel
|
||||
$application->persistentStorages()->delete();
|
||||
$application->environment_variables()->delete();
|
||||
$application->environment_variables_preview()->delete();
|
||||
foreach ($application->scheduled_tasks as $task) {
|
||||
$task->delete();
|
||||
}
|
||||
$application->tags()->detach();
|
||||
});
|
||||
}
|
||||
@@ -200,9 +203,6 @@ class Application extends BaseModel
|
||||
set: fn ($value) => $value === "" ? null : $value,
|
||||
);
|
||||
}
|
||||
|
||||
// Normal Deployments
|
||||
|
||||
public function portsMappingsArray(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
@@ -212,9 +212,13 @@ class Application extends BaseModel
|
||||
|
||||
);
|
||||
}
|
||||
public function isExited()
|
||||
{
|
||||
return (bool) str($this->status)->startsWith('exited');
|
||||
}
|
||||
public function realStatus()
|
||||
{
|
||||
return $this->getRawOriginal('status');
|
||||
return $this->getRawOriginal('status');
|
||||
}
|
||||
public function status(): Attribute
|
||||
{
|
||||
@@ -269,15 +273,11 @@ class Application extends BaseModel
|
||||
foreach ($additional_servers_status as $status) {
|
||||
$server_status = str($status)->before(':')->value();
|
||||
$server_health = str($status)->after(':')->value() ?? 'unhealthy';
|
||||
if ($server_status !== 'running') {
|
||||
if ($main_server_status !== $server_status) {
|
||||
$complex_status = 'degraded';
|
||||
}
|
||||
if ($main_server_status !== $server_status) {
|
||||
$complex_status = 'degraded';
|
||||
}
|
||||
if ($server_health !== 'healthy') {
|
||||
if ($main_server_health !== $server_health) {
|
||||
$complex_health = 'unhealthy';
|
||||
}
|
||||
if ($main_server_health !== $server_health) {
|
||||
$complex_health = 'unhealthy';
|
||||
}
|
||||
}
|
||||
return "$complex_status:$complex_health";
|
||||
@@ -470,7 +470,7 @@ class Application extends BaseModel
|
||||
{
|
||||
return data_get($this, 'settings.is_log_drain_enabled', false);
|
||||
}
|
||||
public function isConfigurationChanged($save = false)
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
||||
if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
|
||||
@@ -518,17 +518,21 @@ class Application extends BaseModel
|
||||
{
|
||||
return "/artifacts/{$uuid}";
|
||||
}
|
||||
function setGitImportSettings(string $deployment_uuid, string $git_clone_command)
|
||||
function setGitImportSettings(string $deployment_uuid, string $git_clone_command, bool $public = false)
|
||||
{
|
||||
$baseDir = $this->generateBaseDir($deployment_uuid);
|
||||
|
||||
if ($this->git_commit_sha !== 'HEAD') {
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && git -c advice.detachedHead=false checkout {$this->git_commit_sha} >/dev/null 2>&1";
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git -c advice.detachedHead=false checkout {$this->git_commit_sha} >/dev/null 2>&1";
|
||||
}
|
||||
if ($this->settings->is_git_submodules_enabled) {
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && git submodule update --init --recursive";
|
||||
if ($public) {
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && sed -i \"s#git@\(.*\):#https://\\1/#g\" {$baseDir}/.gitmodules || true";
|
||||
}
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git submodule update --init --recursive";
|
||||
}
|
||||
if ($this->settings->is_git_lfs_enabled) {
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && git lfs pull";
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git lfs pull";
|
||||
}
|
||||
return $git_clone_command;
|
||||
}
|
||||
@@ -556,7 +560,7 @@ class Application extends BaseModel
|
||||
$fullRepoUrl = "{$this->source->html_url}/{$customRepository}";
|
||||
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$customRepository} {$baseDir}";
|
||||
if (!$only_checkout) {
|
||||
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command);
|
||||
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: true);
|
||||
}
|
||||
if ($exec_in_docker) {
|
||||
$commands->push(executeInDocker($deployment_uuid, $git_clone_command));
|
||||
@@ -655,7 +659,7 @@ class Application extends BaseModel
|
||||
if ($this->deploymentType() === 'other') {
|
||||
$fullRepoUrl = $customRepository;
|
||||
$git_clone_command = "{$git_clone_command} {$customRepository} {$baseDir}";
|
||||
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command);
|
||||
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: true);
|
||||
|
||||
if ($pull_request_id !== 0) {
|
||||
if ($git_type === 'gitlab') {
|
||||
|
||||
@@ -26,7 +26,6 @@ class Environment extends Model
|
||||
{
|
||||
return $this->hasMany(Application::class);
|
||||
}
|
||||
|
||||
public function postgresqls()
|
||||
{
|
||||
return $this->hasMany(StandalonePostgresql::class);
|
||||
|
||||
@@ -10,20 +10,37 @@ class LocalFileVolume extends BaseModel
|
||||
use HasFactory;
|
||||
protected $guarded = [];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function (LocalFileVolume $fileVolume) {
|
||||
$fileVolume->load(['service']);
|
||||
$fileVolume->saveStorageOnServer();
|
||||
});
|
||||
}
|
||||
public function service()
|
||||
{
|
||||
return $this->morphTo('resource');
|
||||
}
|
||||
public function saveStorageOnServer(ServiceApplication|ServiceDatabase $service)
|
||||
public function saveStorageOnServer()
|
||||
{
|
||||
$workdir = $service->service->workdir();
|
||||
$server = $service->service->server;
|
||||
$workdir = $this->resource->service->workdir();
|
||||
$server = $this->resource->service->server;
|
||||
$commands = collect([
|
||||
"mkdir -p $workdir > /dev/null 2>&1 || true",
|
||||
"cd $workdir"
|
||||
]);
|
||||
$is_directory = $this->is_directory;
|
||||
if ($is_directory) {
|
||||
$commands->push("mkdir -p $this->fs_path > /dev/null 2>&1 || true");
|
||||
}
|
||||
if (str($this->fs_path)->startsWith('.') || str($this->fs_path)->startsWith('/') || str($this->fs_path)->startsWith('~')) {
|
||||
$parent_dir = str($this->fs_path)->beforeLast('/');
|
||||
if ($parent_dir != '') {
|
||||
$commands->push("mkdir -p $parent_dir > /dev/null 2>&1 || true");
|
||||
}
|
||||
}
|
||||
$fileVolume = $this;
|
||||
$path = Str::of(data_get($fileVolume, 'fs_path'));
|
||||
$path = str(data_get($fileVolume, 'fs_path'));
|
||||
$content = data_get($fileVolume, 'content');
|
||||
if ($path->startsWith('.')) {
|
||||
$path = $path->after('.');
|
||||
@@ -32,17 +49,19 @@ class LocalFileVolume extends BaseModel
|
||||
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
|
||||
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
|
||||
if ($isFile == 'OK' && $fileVolume->is_directory) {
|
||||
throw new \Exception("File $path is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.");
|
||||
throw new \Exception("The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.");
|
||||
} else if ($isDir == 'OK' && !$fileVolume->is_directory) {
|
||||
throw new \Exception("File $path is a directory on the server, but you are trying to mark it as a file. Please delete the directory on the server or mark it as directory.");
|
||||
throw new \Exception("The following file is a directory on the server, but you are trying to mark it as a file. <br><br>Please delete the directory on the server or mark it as directory.");
|
||||
}
|
||||
if (!$fileVolume->is_directory && $isDir == 'NOK') {
|
||||
$content = base64_encode($content);
|
||||
$commands->push("echo '$content' | base64 -d > $path");
|
||||
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
|
||||
if ($content) {
|
||||
$content = base64_encode($content);
|
||||
$commands->push("echo '$content' | base64 -d > $path");
|
||||
$commands->push("chmod +x $path");
|
||||
}
|
||||
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
|
||||
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
|
||||
}
|
||||
ray($commands->toArray());
|
||||
return instant_remote_process($commands, $server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ProjectSetting extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'project_id'
|
||||
];
|
||||
protected $guarded = [];
|
||||
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +225,32 @@ class Server extends BaseModel
|
||||
$services = $this->services();
|
||||
return $applications->concat($databases)->concat($services->get());
|
||||
}
|
||||
public function stopUnmanaged($id)
|
||||
{
|
||||
return instant_remote_process(["docker stop -t 0 $id"], $this);
|
||||
}
|
||||
public function restartUnmanaged($id)
|
||||
{
|
||||
return instant_remote_process(["docker restart $id"], $this);
|
||||
}
|
||||
public function startUnmanaged($id)
|
||||
{
|
||||
return instant_remote_process(["docker start $id"], $this);
|
||||
}
|
||||
public function loadUnmanagedContainers()
|
||||
{
|
||||
$containers = instant_remote_process(["docker ps -a --format '{{json .}}' "], $this);
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
$containers = $containers->map(function ($container) {
|
||||
$labels = data_get($container, 'Labels');
|
||||
if (!str($labels)->contains("coolify.managed")) {
|
||||
return $container;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
$containers = $containers->filter();
|
||||
return collect($containers);
|
||||
}
|
||||
public function hasDefinedResources()
|
||||
{
|
||||
$applications = $this->applications()->count() > 0;
|
||||
@@ -245,6 +271,8 @@ class Server extends BaseModel
|
||||
$mysqls = data_get($standaloneDocker, 'mysqls', collect([]));
|
||||
$mariadbs = data_get($standaloneDocker, 'mariadbs', collect([]));
|
||||
return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
|
||||
})->filter(function ($item) {
|
||||
return data_get($item, 'name') !== 'coolify-db';
|
||||
})->flatten();
|
||||
}
|
||||
public function applications()
|
||||
@@ -370,10 +398,10 @@ class Server extends BaseModel
|
||||
}
|
||||
});
|
||||
if ($supported->count() === 1) {
|
||||
ray('supported');
|
||||
// ray('supported');
|
||||
return str($supported->first());
|
||||
} else {
|
||||
ray('not supported');
|
||||
// ray('not supported');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -400,7 +428,9 @@ class Server extends BaseModel
|
||||
if ($server->skipServer()) {
|
||||
return false;
|
||||
}
|
||||
$uptime = instant_remote_process(['uptime'], $server, false);
|
||||
// EC2 does not have `uptime` command, lol
|
||||
|
||||
$uptime = instant_remote_process(['ls /'], $server, false);
|
||||
if (!$uptime) {
|
||||
$server->settings()->update([
|
||||
'is_reachable' => false,
|
||||
@@ -438,11 +468,36 @@ class Server extends BaseModel
|
||||
}
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
instant_remote_process(["docker version"], $this);
|
||||
} catch (\Throwable $e) {
|
||||
$this->settings->is_usable = false;
|
||||
$this->settings->save();
|
||||
if ($throwError) {
|
||||
throw new \Exception('Server is not usable. Docker Engine is not running.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$this->settings->is_usable = true;
|
||||
$this->settings->save();
|
||||
$this->validateCoolifyNetwork(isSwarm: false, isBuildServer: $this->settings->is_build_server);
|
||||
return true;
|
||||
}
|
||||
public function validateDockerCompose($throwError = false)
|
||||
{
|
||||
$dockerCompose = instant_remote_process(["docker compose version"], $this, false);
|
||||
if (is_null($dockerCompose)) {
|
||||
$this->settings->is_usable = false;
|
||||
$this->settings->save();
|
||||
if ($throwError) {
|
||||
throw new \Exception('Server is not usable. Docker Compose is not installed.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$this->settings->is_usable = true;
|
||||
$this->settings->save();
|
||||
return true;
|
||||
}
|
||||
public function validateDockerSwarm()
|
||||
{
|
||||
$swarmStatus = instant_remote_process(["docker info|grep -i swarm"], $this, false);
|
||||
|
||||
@@ -28,6 +28,48 @@ class Service extends BaseModel
|
||||
{
|
||||
return $this->morphToMany(Tag::class, 'taggable');
|
||||
}
|
||||
public function status() {
|
||||
$foundRunning = false;
|
||||
$isDegraded = false;
|
||||
$foundRestaring = false;
|
||||
$applications = $this->applications;
|
||||
$databases = $this->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';
|
||||
}
|
||||
public function extraFields()
|
||||
{
|
||||
$fields = collect([]);
|
||||
|
||||
@@ -23,6 +23,10 @@ class ServiceApplication extends BaseModel
|
||||
{
|
||||
return data_get($this, 'is_log_drain_enabled', false);
|
||||
}
|
||||
public function isGzipEnabled()
|
||||
{
|
||||
return data_get($this, 'is_gzip_enabled', true);
|
||||
}
|
||||
public function type()
|
||||
{
|
||||
return 'service';
|
||||
|
||||
@@ -21,6 +21,10 @@ class ServiceDatabase extends BaseModel
|
||||
{
|
||||
return data_get($this, 'is_log_drain_enabled', false);
|
||||
}
|
||||
public function isGzipEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public function type()
|
||||
{
|
||||
return 'service';
|
||||
|
||||
@@ -82,6 +82,9 @@ class StandaloneMariadb extends BaseModel
|
||||
{
|
||||
return $this->morphToMany(Tag::class, 'taggable');
|
||||
}
|
||||
public function project() {
|
||||
return data_get($this, 'environment.project');
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
|
||||
@@ -85,6 +85,9 @@ class StandaloneMongodb extends BaseModel
|
||||
{
|
||||
return $this->morphToMany(Tag::class, 'taggable');
|
||||
}
|
||||
public function project() {
|
||||
return data_get($this, 'environment.project');
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
|
||||
@@ -82,6 +82,9 @@ class StandaloneMysql extends BaseModel
|
||||
{
|
||||
return $this->morphToMany(Tag::class, 'taggable');
|
||||
}
|
||||
public function project() {
|
||||
return data_get($this, 'environment.project');
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
|
||||
@@ -82,6 +82,10 @@ class StandalonePostgresql extends BaseModel
|
||||
{
|
||||
return $this->morphToMany(Tag::class, 'taggable');
|
||||
}
|
||||
public function project()
|
||||
{
|
||||
return data_get($this, 'environment.project');
|
||||
}
|
||||
public function link()
|
||||
{
|
||||
if (data_get($this, 'environment.project.uuid')) {
|
||||
|
||||
@@ -77,6 +77,10 @@ class StandaloneRedis extends BaseModel
|
||||
{
|
||||
return $this->morphToMany(Tag::class, 'taggable');
|
||||
}
|
||||
public function project()
|
||||
{
|
||||
return data_get($this, 'environment.project');
|
||||
}
|
||||
public function team()
|
||||
{
|
||||
return data_get($this, 'environment.project.team');
|
||||
|
||||
31
app/View/Components/ResourceView.php
Normal file
31
app/View/Components/ResourceView.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class ResourceView extends Component
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public ?string $wire = null,
|
||||
public ?string $logo = null,
|
||||
public ?string $documentation = null,
|
||||
public bool $upgrade = false,
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.resource-view');
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,11 @@ class Index extends Component
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
|
||||
public function __construct(
|
||||
public string $status = 'exited',
|
||||
)
|
||||
{
|
||||
//
|
||||
public $resource = null,
|
||||
public bool $showRefreshButton = true,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,8 +15,9 @@ class Services extends Component
|
||||
public function __construct(
|
||||
public Service $service,
|
||||
public string $complexStatus = 'exited',
|
||||
public bool $showRefreshButton = true
|
||||
) {
|
||||
$this->complexStatus = serviceStatus($service);
|
||||
$this->complexStatus = $service->status();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
7
bootstrap/helpers/api.php
Normal file
7
bootstrap/helpers/api.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
function get_team_id_from_token()
|
||||
{
|
||||
$token = auth()->user()->currentAccessToken();
|
||||
return data_get($token, 'team_id');
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
function queue_application_deployment(Application $application, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, Server $server = null, StandaloneDocker $destination = null)
|
||||
function queue_application_deployment(Application $application, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, Server $server = null, StandaloneDocker $destination = null, bool $only_this_server = false)
|
||||
{
|
||||
$application_id = $application->id;
|
||||
$deployment_link = Url::fromString($application->link() . "/deployment/{$deployment_uuid}");
|
||||
@@ -37,7 +37,8 @@ function queue_application_deployment(Application $application, string $deployme
|
||||
'is_webhook' => $is_webhook,
|
||||
'restart_only' => $restart_only,
|
||||
'commit' => $commit,
|
||||
'git_type' => $git_type
|
||||
'git_type' => $git_type,
|
||||
'only_this_server' => $only_this_server
|
||||
]);
|
||||
|
||||
if ($no_questions_asked) {
|
||||
|
||||
@@ -13,6 +13,11 @@ const VALID_CRON_STRINGS = [
|
||||
const RESTART_MODE = 'unless-stopped';
|
||||
|
||||
const DATABASE_DOCKER_IMAGES = [
|
||||
'bitnami/mariadb',
|
||||
'bitnami/mongodb',
|
||||
'bitnami/mysql',
|
||||
'bitnami/postgresql',
|
||||
'bitnami/redis',
|
||||
'mysql',
|
||||
'mariadb',
|
||||
'postgres',
|
||||
|
||||
@@ -7,7 +7,6 @@ use App\Models\ServiceApplication;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Url\Url;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null): Collection
|
||||
{
|
||||
@@ -123,10 +122,14 @@ function getContainerStatus(Server $server, string $container_id, bool $all_data
|
||||
|
||||
function generateApplicationContainerName(Application $application, $pull_request_id = 0)
|
||||
{
|
||||
$consistent_container_name = $application->settings->is_consistent_container_name_enabled;
|
||||
$now = now()->format('Hisu');
|
||||
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
||||
return $application->uuid . '-pr-' . $pull_request_id;
|
||||
} else {
|
||||
if ($consistent_container_name) {
|
||||
return $application->uuid;
|
||||
}
|
||||
return $application->uuid . '-' . $now;
|
||||
}
|
||||
}
|
||||
@@ -209,15 +212,48 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource,
|
||||
}
|
||||
return $payload;
|
||||
}
|
||||
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null)
|
||||
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?string $service_name = null)
|
||||
{
|
||||
$labels = collect([]);
|
||||
$labels->push('traefik.enable=true');
|
||||
$labels->push("traefik.http.middlewares.gzip.compress=true");
|
||||
$labels->push("traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https");
|
||||
|
||||
$basic_auth = false;
|
||||
$basic_auth_middleware = null;
|
||||
$redirect = false;
|
||||
$redirect_middleware = null;
|
||||
if ($serviceLabels) {
|
||||
$basic_auth = $serviceLabels->contains(function ($value) {
|
||||
return str_contains($value, 'basicauth');
|
||||
});
|
||||
if ($basic_auth) {
|
||||
$basic_auth_middleware = $serviceLabels
|
||||
->map(function ($item) {
|
||||
if (preg_match('/traefik\.http\.middlewares\.(.*?)\.basicauth\.users/', $item, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
})
|
||||
->filter()
|
||||
->first();
|
||||
}
|
||||
$redirect = $serviceLabels->contains(function ($value) {
|
||||
return str_contains($value, 'redirectregex');
|
||||
});
|
||||
if ($redirect) {
|
||||
$redirect_middleware = $serviceLabels
|
||||
->map(function ($item) {
|
||||
if (preg_match('/traefik\.http\.middlewares\.(.*?)\.redirectregex\.regex/', $item, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
})
|
||||
->filter()
|
||||
->first();
|
||||
}
|
||||
}
|
||||
foreach ($domains as $loop => $domain) {
|
||||
try {
|
||||
$uuid = new Cuid2(7);
|
||||
// $uuid = new Cuid2(7);
|
||||
$url = Url::fromString($domain);
|
||||
$host = $url->getHost();
|
||||
$path = $url->getPath();
|
||||
@@ -228,6 +264,10 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
}
|
||||
$http_label = "http-{$loop}-{$uuid}";
|
||||
$https_label = "https-{$loop}-{$uuid}";
|
||||
if ($service_name) {
|
||||
$http_label = "http-{$loop}-{$uuid}-{$service_name}";
|
||||
$https_label = "https-{$loop}-{$uuid}-{$service_name}";
|
||||
}
|
||||
|
||||
if ($schema === 'https') {
|
||||
// Set labels for https
|
||||
@@ -239,11 +279,36 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
}
|
||||
if ($path !== '/') {
|
||||
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
|
||||
$labels->push("traefik.http.routers.{$https_label}.middlewares={$https_label}-stripprefix,gzip");
|
||||
$middlewares = collect(["{$https_label}-stripprefix"]);
|
||||
if ($is_gzip_enabled) {
|
||||
$middlewares->push('gzip');
|
||||
}
|
||||
if ($basic_auth && $basic_auth_middleware) {
|
||||
$middlewares->push($basic_auth_middleware);
|
||||
}
|
||||
if ($redirect && $redirect_middleware) {
|
||||
$middlewares->push($redirect_middleware);
|
||||
}
|
||||
if ($middlewares->isNotEmpty()) {
|
||||
$middlewares = $middlewares->join(',');
|
||||
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
|
||||
}
|
||||
} else {
|
||||
$labels->push("traefik.http.routers.{$https_label}.middlewares=gzip");
|
||||
$middlewares = collect([]);
|
||||
if ($is_gzip_enabled) {
|
||||
$middlewares->push('gzip');
|
||||
}
|
||||
if ($basic_auth && $basic_auth_middleware) {
|
||||
$middlewares->push($basic_auth_middleware);
|
||||
}
|
||||
if ($redirect && $redirect_middleware) {
|
||||
$middlewares->push($redirect_middleware);
|
||||
}
|
||||
if ($middlewares->isNotEmpty()) {
|
||||
$middlewares = $middlewares->join(',');
|
||||
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
|
||||
}
|
||||
}
|
||||
|
||||
$labels->push("traefik.http.routers.{$https_label}.tls=true");
|
||||
$labels->push("traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt");
|
||||
|
||||
@@ -267,16 +332,41 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
}
|
||||
if ($path !== '/') {
|
||||
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
|
||||
$labels->push("traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix,gzip");
|
||||
$middlewares = collect(["{$http_label}-stripprefix"]);
|
||||
if ($is_gzip_enabled) {
|
||||
$middlewares->push('gzip');
|
||||
}
|
||||
if ($basic_auth && $basic_auth_middleware) {
|
||||
$middlewares->push($basic_auth_middleware);
|
||||
}
|
||||
if ($redirect && $redirect_middleware) {
|
||||
$middlewares->push($redirect_middleware);
|
||||
}
|
||||
if ($middlewares->isNotEmpty()) {
|
||||
$middlewares = $middlewares->join(',');
|
||||
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
|
||||
}
|
||||
} else {
|
||||
$labels->push("traefik.http.routers.{$http_label}.middlewares=gzip");
|
||||
$middlewares = collect([]);
|
||||
if ($is_gzip_enabled) {
|
||||
$middlewares->push('gzip');
|
||||
}
|
||||
if ($basic_auth && $basic_auth_middleware) {
|
||||
$middlewares->push($basic_auth_middleware);
|
||||
}
|
||||
if ($redirect && $redirect_middleware) {
|
||||
$middlewares->push($redirect_middleware);
|
||||
}
|
||||
if ($middlewares->isNotEmpty()) {
|
||||
$middlewares = $middlewares->join(',');
|
||||
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $labels->sort();
|
||||
}
|
||||
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
|
||||
@@ -340,25 +430,20 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
|
||||
'--cap-drop' => 'cap_drop',
|
||||
'--security-opt' => 'security_opt',
|
||||
'--sysctl' => 'sysctls',
|
||||
'--device' => 'devices',
|
||||
'--ulimit' => 'ulimits',
|
||||
'--device' => 'devices',
|
||||
'--init' => 'init',
|
||||
'--ulimit' => 'ulimits',
|
||||
'--privileged' => 'privileged',
|
||||
]);
|
||||
foreach ($matches as $match) {
|
||||
$option = $match[1];
|
||||
$value = isset($match[2]) && $match[2] !== '' ? $match[2] : true;
|
||||
if ($list_options->contains($option)) {
|
||||
$value = explode(',', $value);
|
||||
}
|
||||
if (array_key_exists($option, $options)) {
|
||||
if (is_array($options[$option])) {
|
||||
$options[$option][] = $value;
|
||||
} else {
|
||||
$options[$option] = [$options[$option], $value];
|
||||
}
|
||||
if (isset($match[2]) && $match[2] !== '') {
|
||||
$value = $match[2];
|
||||
$options[$option][] = $value;
|
||||
$options[$option] = array_unique($options[$option]);
|
||||
} else {
|
||||
$value = true;
|
||||
$options[$option] = $value;
|
||||
}
|
||||
}
|
||||
@@ -370,7 +455,7 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
|
||||
}
|
||||
if ($option === '--ulimit') {
|
||||
$ulimits = collect([]);
|
||||
collect($value)->map(function ($ulimit) use ($ulimits){
|
||||
collect($value)->map(function ($ulimit) use ($ulimits) {
|
||||
$ulimit = explode('=', $ulimit);
|
||||
$type = $ulimit[0];
|
||||
$limits = explode(':', $ulimit[1]);
|
||||
@@ -381,7 +466,6 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
|
||||
'soft' => $soft_limit,
|
||||
'hard' => $hard_limit
|
||||
]);
|
||||
|
||||
} else {
|
||||
$soft_limit = $ulimit[1];
|
||||
$ulimits->put($type, [
|
||||
|
||||
@@ -86,3 +86,8 @@ function get_installation_path(GithubApp $source)
|
||||
$installation_path = $github->html_url === 'https://github.com' ? 'apps' : 'github-apps';
|
||||
return "$github->html_url/$installation_path/$name/installations/new";
|
||||
}
|
||||
function get_permissions_path(GithubApp $source) {
|
||||
$github = GithubApp::where('uuid', $source->uuid)->first();
|
||||
$name = Str::of(Str::kebab($github->name));
|
||||
return "$github->html_url/settings/apps/$name/permissions";
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ function generate_default_proxy_configuration(Server $server)
|
||||
"traefik.http.routers.traefik.entrypoints=http",
|
||||
"traefik.http.routers.traefik.service=api@internal",
|
||||
"traefik.http.services.traefik.loadbalancer.server.port=8080",
|
||||
"coolify.managed=true",
|
||||
];
|
||||
$config = [
|
||||
"version" => "3.8",
|
||||
@@ -139,7 +140,9 @@ function generate_default_proxy_configuration(Server $server)
|
||||
"--entrypoints.http.address=:80",
|
||||
"--entrypoints.https.address=:443",
|
||||
"--entrypoints.http.http.encodequerysemicolons=true",
|
||||
"--entryPoints.http.http2.maxConcurrentStreams=50",
|
||||
"--entrypoints.https.http.encodequerysemicolons=true",
|
||||
"--entryPoints.https.http2.maxConcurrentStreams=50",
|
||||
"--providers.docker.exposedbydefault=false",
|
||||
"--providers.file.directory=/traefik/dynamic/",
|
||||
"--providers.file.watch=true",
|
||||
|
||||
@@ -228,56 +228,6 @@ function refresh_server_connection(?PrivateKey $private_key = null)
|
||||
}
|
||||
}
|
||||
|
||||
// function validateServer(Server $server, bool $throwError = false)
|
||||
// {
|
||||
// try {
|
||||
// $uptime = instant_remote_process(['uptime'], $server, $throwError);
|
||||
// if (!$uptime) {
|
||||
// $server->settings->is_reachable = false;
|
||||
// $server->team->notify(new Unreachable($server));
|
||||
// $server->unreachable_notification_sent = true;
|
||||
// $server->save();
|
||||
// return [
|
||||
// "uptime" => null,
|
||||
// "dockerVersion" => null,
|
||||
// ];
|
||||
// }
|
||||
// $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);
|
||||
// if (!$dockerVersion) {
|
||||
// $dockerVersion = null;
|
||||
// return [
|
||||
// "uptime" => $uptime,
|
||||
// "dockerVersion" => null,
|
||||
// ];
|
||||
// }
|
||||
// $dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||
// if (is_null($dockerVersion)) {
|
||||
// $server->settings->is_usable = false;
|
||||
// } else {
|
||||
// $server->settings->is_usable = true;
|
||||
// if (data_get($server, 'unreachable_notification_sent') === true) {
|
||||
// $server->team->notify(new Revived($server));
|
||||
// $server->unreachable_notification_sent = false;
|
||||
// $server->save();
|
||||
// }
|
||||
// }
|
||||
// return [
|
||||
// "uptime" => $uptime,
|
||||
// "dockerVersion" => $dockerVersion,
|
||||
// ];
|
||||
// } catch (\Throwable $e) {
|
||||
// $server->settings->is_reachable = false;
|
||||
// $server->settings->is_usable = false;
|
||||
// throw $e;
|
||||
// } finally {
|
||||
// if (data_get($server, 'settings')) {
|
||||
// $server->settings->save();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
function checkRequiredCommands(Server $server)
|
||||
{
|
||||
$commands = collect(["jq", "jc"]);
|
||||
|
||||
@@ -4,8 +4,8 @@ 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 Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
function replaceRegex(?string $name = null)
|
||||
@@ -21,49 +21,6 @@ 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 getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneService, bool $isInit = false)
|
||||
{
|
||||
// TODO: make this async
|
||||
@@ -138,12 +95,17 @@ function updateCompose($resource)
|
||||
// Update FQDN
|
||||
$variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper();
|
||||
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
||||
$fqdn = Url::fromString($resource->fqdn);
|
||||
$fqdn = $fqdn->getScheme() . '://' . $fqdn->getHost();
|
||||
if ($generatedEnv) {
|
||||
$generatedEnv->value = $resource->fqdn;
|
||||
$generatedEnv->value = $fqdn;
|
||||
$generatedEnv->save();
|
||||
}
|
||||
$variableName = "SERVICE_URL_" . Str::of($resource->name)->upper();
|
||||
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
||||
$url = Url::fromString($resource->fqdn);
|
||||
$url = $url->getHost();
|
||||
ray($url);
|
||||
if ($generatedEnv) {
|
||||
$url = Str::of($resource->fqdn)->after('://');
|
||||
$generatedEnv->value = $url;
|
||||
|
||||
@@ -104,13 +104,13 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n
|
||||
ray($error);
|
||||
if ($error instanceof TooManyRequestsException) {
|
||||
if (isset($livewire)) {
|
||||
return $livewire->dispatch('error', "Too many requests.", "Please try again in {$error->secondsUntilAvailable} seconds.");
|
||||
return $livewire->dispatch('error', 'Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.');
|
||||
}
|
||||
return "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.";
|
||||
}
|
||||
if ($error instanceof UniqueConstraintViolationException) {
|
||||
if (isset($livewire)) {
|
||||
return $livewire->dispatch('error', "Duplicate entry found.", "Please use a different name.");
|
||||
return $livewire->dispatch('error', 'Duplicate entry found. Please use a different name.');
|
||||
}
|
||||
return "Duplicate entry found. Please use a different name.";
|
||||
}
|
||||
@@ -527,28 +527,32 @@ function getTopLevelNetworks(Service|Application $resource)
|
||||
$definedNetwork = collect([$resource->uuid]);
|
||||
$services = collect($services)->map(function ($service, $_) use ($topLevelNetworks, $definedNetwork) {
|
||||
$serviceNetworks = collect(data_get($service, 'networks', []));
|
||||
$hasHostNetworkMode = data_get($service, 'network_mode') === 'host' ? true : false;
|
||||
|
||||
// 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);
|
||||
// Only add 'networks' key if 'network_mode' is not 'host'
|
||||
if (!$hasHostNetworkMode) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||
return $value == $definedNetwork;
|
||||
});
|
||||
if (!$definedNetworkExists) {
|
||||
foreach ($definedNetwork as $network) {
|
||||
$topLevelNetworks->put($network, [
|
||||
'name' => $network,
|
||||
'external' => true
|
||||
]);
|
||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||
return $value == $definedNetwork;
|
||||
});
|
||||
if (!$definedNetworkExists) {
|
||||
foreach ($definedNetwork as $network) {
|
||||
$topLevelNetworks->put($network, [
|
||||
'name' => $network,
|
||||
'external' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,6 +632,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$serviceNetworks = collect(data_get($service, 'networks', []));
|
||||
$serviceVariables = collect(data_get($service, 'environment', []));
|
||||
$serviceLabels = collect(data_get($service, 'labels', []));
|
||||
$hasHostNetworkMode = data_get($service, 'network_mode') === 'host' ? true : false;
|
||||
if ($serviceLabels->count() > 0) {
|
||||
$removedLabels = collect([]);
|
||||
$serviceLabels = $serviceLabels->filter(function ($serviceLabel, $serviceLabelName) use ($removedLabels) {
|
||||
@@ -698,7 +703,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$savedService->image = $image;
|
||||
$savedService->save();
|
||||
}
|
||||
|
||||
// Collect/create/update networks
|
||||
if ($serviceNetworks->count() > 0) {
|
||||
foreach ($serviceNetworks as $networkName => $networkDetails) {
|
||||
@@ -729,37 +733,39 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$savedService->ports = $collectedPorts->implode(',');
|
||||
$savedService->save();
|
||||
|
||||
// Add Coolify specific networks
|
||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||
return $value == $definedNetwork;
|
||||
});
|
||||
if (!$definedNetworkExists) {
|
||||
foreach ($definedNetwork as $network) {
|
||||
$topLevelNetworks->put($network, [
|
||||
'name' => $network,
|
||||
'external' => true
|
||||
]);
|
||||
if (!$hasHostNetworkMode) {
|
||||
// Add Coolify specific networks
|
||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||
return $value == $definedNetwork;
|
||||
});
|
||||
if (!$definedNetworkExists) {
|
||||
foreach ($definedNetwork as $network) {
|
||||
$topLevelNetworks->put($network, [
|
||||
'name' => $network,
|
||||
'external' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$networks = collect();
|
||||
foreach ($serviceNetworks as $key => $serviceNetwork) {
|
||||
if (gettype($serviceNetwork) === 'string') {
|
||||
// networks:
|
||||
// - appwrite
|
||||
$networks->put($serviceNetwork, null);
|
||||
} else if (gettype($serviceNetwork) === 'array') {
|
||||
// networks:
|
||||
// default:
|
||||
// ipv4_address: 192.168.203.254
|
||||
// $networks->put($serviceNetwork, null);
|
||||
ray($key);
|
||||
$networks->put($key, $serviceNetwork);
|
||||
$networks = collect();
|
||||
foreach ($serviceNetworks as $key => $serviceNetwork) {
|
||||
if (gettype($serviceNetwork) === 'string') {
|
||||
// networks:
|
||||
// - appwrite
|
||||
$networks->put($serviceNetwork, null);
|
||||
} else if (gettype($serviceNetwork) === 'array') {
|
||||
// networks:
|
||||
// default:
|
||||
// ipv4_address: 192.168.203.254
|
||||
// $networks->put($serviceNetwork, null);
|
||||
ray($key);
|
||||
$networks->put($key, $serviceNetwork);
|
||||
}
|
||||
}
|
||||
foreach ($definedNetwork as $key => $network) {
|
||||
$networks->put($network, null);
|
||||
}
|
||||
data_set($service, 'networks', $networks->toArray());
|
||||
}
|
||||
foreach ($definedNetwork as $key => $network) {
|
||||
$networks->put($network, null);
|
||||
}
|
||||
data_set($service, 'networks', $networks->toArray());
|
||||
|
||||
// Collect/create/update volumes
|
||||
if ($serviceVolumes->count() > 0) {
|
||||
@@ -782,14 +788,14 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$source = data_get_str($volume, 'source');
|
||||
$target = data_get_str($volume, 'target');
|
||||
$content = data_get($volume, 'content');
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', false);
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', false) || (bool) data_get($volume, 'is_directory', false);
|
||||
$foundConfig = $savedService->fileStorages()->whereMountPath($target)->first();
|
||||
if ($foundConfig) {
|
||||
$contentNotNull = data_get($foundConfig, 'content');
|
||||
if ($contentNotNull) {
|
||||
$content = $contentNotNull;
|
||||
}
|
||||
$isDirectory = (bool) data_get($foundConfig, 'is_directory');
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', false) || (bool) data_get($volume, 'is_directory', false);
|
||||
}
|
||||
}
|
||||
if ($type->value() === 'bind') {
|
||||
@@ -966,7 +972,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
]);
|
||||
}
|
||||
if (!$isDatabase) {
|
||||
if ($command->value() === 'FQDN' && is_null($savedService->fqdn)) {
|
||||
if ($command->value() === 'FQDN' && is_null($savedService->fqdn) && !$foundEnv) {
|
||||
$savedService->fqdn = $fqdn;
|
||||
$savedService->save();
|
||||
}
|
||||
@@ -1030,7 +1036,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||
if (!$isDatabase && $fqdns->count() > 0) {
|
||||
if ($fqdns) {
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($resource->uuid, $fqdns, true));
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($resource->uuid, $fqdns, true, serviceLabels: $serviceLabels, is_gzip_enabled: $savedService->isGzipEnabled(), service_name: $serviceName));
|
||||
}
|
||||
}
|
||||
if ($resource->server->isLogDrainEnabled() && $savedService->isLogDrainEnabled()) {
|
||||
@@ -1054,6 +1060,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
data_set($service, 'container_name', $containerName);
|
||||
data_forget($service, 'volumes.*.content');
|
||||
data_forget($service, 'volumes.*.isDirectory');
|
||||
data_forget($service, 'volumes.*.is_directory');
|
||||
|
||||
// Remove unnecessary variables from service.environment
|
||||
// $withoutServiceEnvs = collect([]);
|
||||
// collect(data_get($service, 'environment'))->each(function ($value, $key) use ($withoutServiceEnvs) {
|
||||
@@ -1469,7 +1477,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
return $preview_fqdn;
|
||||
});
|
||||
}
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($uuid, $fqdns));
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($uuid, $fqdns, serviceLabels: $serviceLabels));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
803
composer.lock
generated
803
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ return [
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.212',
|
||||
'release' => '4.0.0-beta.223',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.212';
|
||||
return '4.0.0-beta.223';
|
||||
|
||||
@@ -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('application_settings', function (Blueprint $table) {
|
||||
$table->boolean('is_consistent_container_name_enabled')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('is_consistent_container_name_enabled');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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('service_applications', function (Blueprint $table) {
|
||||
$table->boolean('is_gzip_enabled')->default(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('service_applications', function (Blueprint $table) {
|
||||
$table->dropColumn('is_gzip_enabled');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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::table('github_apps', function (Blueprint $table) {
|
||||
$table->string('contents')->nullable();
|
||||
$table->string('metadata')->nullable();
|
||||
$table->string('pull_requests')->nullable();
|
||||
$table->string('administration')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('github_apps', function (Blueprint $table) {
|
||||
$table->dropColumn('contents');
|
||||
$table->dropColumn('metadata');
|
||||
$table->dropColumn('pull_requests');
|
||||
$table->dropColumn('administration');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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('application_deployment_queues', function (Blueprint $table) {
|
||||
$table->boolean('only_this_server')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('application_deployment_queues', function (Blueprint $table) {
|
||||
$table->dropColumn('only_this_server');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
coolify:
|
||||
image: "ghcr.io/coollabsio/coolify:${LATEST_IMAGE:latest}"
|
||||
image: "ghcr.io/coollabsio/coolify:${LATEST_IMAGE:-latest}"
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /data/coolify/source/.env
|
||||
|
||||
176
package-lock.json
generated
176
package-lock.json
generated
@@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@tailwindcss/typography": "0.5.10",
|
||||
"alpinejs": "3.13.5",
|
||||
"daisyui": "4.4.19",
|
||||
"daisyui": "4.7.2",
|
||||
"ioredis": "5.3.2",
|
||||
"tailwindcss-scrollbar": "0.1.0"
|
||||
},
|
||||
@@ -17,11 +17,11 @@
|
||||
"axios": "1.6.7",
|
||||
"laravel-echo": "1.15.3",
|
||||
"laravel-vite-plugin": "0.8.1",
|
||||
"postcss": "8.4.33",
|
||||
"postcss": "8.4.35",
|
||||
"pusher-js": "8.4.0-rc2",
|
||||
"tailwindcss": "3.4.1",
|
||||
"vite": "4.5.2",
|
||||
"vue": "3.4.15"
|
||||
"vue": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@@ -524,77 +524,77 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz",
|
||||
"integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
|
||||
"integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.6",
|
||||
"@vue/shared": "3.4.15",
|
||||
"@babel/parser": "^7.23.9",
|
||||
"@vue/shared": "3.4.19",
|
||||
"entities": "^4.5.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core/node_modules/@vue/shared": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
|
||||
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz",
|
||||
"integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
|
||||
"integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.4.15",
|
||||
"@vue/shared": "3.4.15"
|
||||
"@vue/compiler-core": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
|
||||
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz",
|
||||
"integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz",
|
||||
"integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.6",
|
||||
"@vue/compiler-core": "3.4.15",
|
||||
"@vue/compiler-dom": "3.4.15",
|
||||
"@vue/compiler-ssr": "3.4.15",
|
||||
"@vue/shared": "3.4.15",
|
||||
"@babel/parser": "^7.23.9",
|
||||
"@vue/compiler-core": "3.4.19",
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/compiler-ssr": "3.4.19",
|
||||
"@vue/shared": "3.4.19",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.5",
|
||||
"magic-string": "^0.30.6",
|
||||
"postcss": "^8.4.33",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
|
||||
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz",
|
||||
"integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
|
||||
"integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.15",
|
||||
"@vue/shared": "3.4.15"
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
|
||||
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
@@ -606,64 +606,64 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz",
|
||||
"integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz",
|
||||
"integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.4.15",
|
||||
"@vue/shared": "3.4.15"
|
||||
"@vue/reactivity": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz",
|
||||
"integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz",
|
||||
"integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.4.15"
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
|
||||
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz",
|
||||
"integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz",
|
||||
"integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/runtime-core": "3.4.15",
|
||||
"@vue/shared": "3.4.15",
|
||||
"@vue/runtime-core": "3.4.19",
|
||||
"@vue/shared": "3.4.19",
|
||||
"csstype": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
|
||||
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz",
|
||||
"integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz",
|
||||
"integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.4.15",
|
||||
"@vue/shared": "3.4.15"
|
||||
"@vue/compiler-ssr": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.4.15"
|
||||
"vue": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
|
||||
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
@@ -953,9 +953,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/daisyui": {
|
||||
"version": "4.4.19",
|
||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.4.19.tgz",
|
||||
"integrity": "sha512-IjOLWwnndD4N7Ut5CDxbUsaVtbqXPeVHM92IcgxGFxpuOd3CCKW/PAXZH6JoBTHFRaN57vB9XqEhdWm5yC+bPA==",
|
||||
"version": "4.7.2",
|
||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.7.2.tgz",
|
||||
"integrity": "sha512-9UCss12Zmyk/22u+JbkVrHHxOzFOyY17HuqP5LeswI4hclbj6qbjJTovdj2zRy8cCH6/n6Wh0lTLjriGnyGh0g==",
|
||||
"dependencies": {
|
||||
"css-selector-tokenizer": "^0.8",
|
||||
"culori": "^3",
|
||||
@@ -1410,9 +1410,9 @@
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.5",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
|
||||
"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
|
||||
"version": "0.30.7",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz",
|
||||
"integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||
@@ -1598,9 +1598,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.33",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
|
||||
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
|
||||
"version": "8.4.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
||||
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -2106,16 +2106,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz",
|
||||
"integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz",
|
||||
"integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.15",
|
||||
"@vue/compiler-sfc": "3.4.15",
|
||||
"@vue/runtime-dom": "3.4.15",
|
||||
"@vue/server-renderer": "3.4.15",
|
||||
"@vue/shared": "3.4.15"
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/compiler-sfc": "3.4.19",
|
||||
"@vue/runtime-dom": "3.4.19",
|
||||
"@vue/server-renderer": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "*"
|
||||
@@ -2127,9 +2127,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue/node_modules/@vue/shared": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
|
||||
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
|
||||
@@ -11,16 +11,16 @@
|
||||
"axios": "1.6.7",
|
||||
"laravel-echo": "1.15.3",
|
||||
"laravel-vite-plugin": "0.8.1",
|
||||
"postcss": "8.4.33",
|
||||
"postcss": "8.4.35",
|
||||
"pusher-js": "8.4.0-rc2",
|
||||
"tailwindcss": "3.4.1",
|
||||
"vite": "4.5.2",
|
||||
"vue": "3.4.15"
|
||||
"vue": "3.4.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/typography": "0.5.10",
|
||||
"alpinejs": "3.13.5",
|
||||
"daisyui": "4.4.19",
|
||||
"daisyui": "4.7.2",
|
||||
"ioredis": "5.3.2",
|
||||
"tailwindcss-scrollbar": "0.1.0"
|
||||
}
|
||||
|
||||
19
public/svgs/appsmith.svg
Normal file
19
public/svgs/appsmith.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="924" height="235" viewBox="0 0 924 235" fill="ffffff" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1_41)">
|
||||
<path d="M602.936 59.0637C601.82 58.0005 600.937 56.7175 600.342 55.2956C599.746 53.8738 599.452 52.344 599.478 50.8028C599.478 47.673 600.722 44.6712 602.935 42.4581C605.148 40.2449 608.15 39.0016 611.28 39.0016C612.821 38.9758 614.351 39.2697 615.772 39.8648C617.194 40.46 618.477 41.3434 619.54 42.4593C620.635 43.5541 621.498 44.8581 622.078 46.2936C622.658 47.7291 622.943 49.2667 622.916 50.8146C622.941 52.3558 622.648 53.8856 622.052 55.3074C621.457 56.7293 620.574 58.0123 619.458 59.0755C618.389 60.1594 617.114 61.0169 615.706 61.5968C614.299 62.1767 612.79 62.4671 611.268 62.4507C609.723 62.4725 608.19 62.184 606.758 61.6022C605.327 61.0204 604.027 60.1572 602.936 59.0637V59.0637Z" fill="#ffffff"/>
|
||||
<path d="M62.7915 160.047C61.7766 158.246 60.9899 154.705 60.4313 149.426C54.5778 157.876 45.2351 162.097 32.4032 162.089C22.8284 162.089 15.2009 159.784 9.52053 155.173C3.84018 150.563 1 144.143 1 135.914C1 120.045 12.1443 110.997 34.433 108.771L47.6033 107.591C51.9934 107.036 55.1443 105.986 57.0443 104.475C58.023 103.637 58.7921 102.581 59.29 101.393C59.788 100.204 60.001 98.9159 59.912 97.6303C59.912 93.9169 58.7043 91.1868 56.289 89.4402C53.8737 87.6936 49.7904 86.8204 44.0393 86.8204C37.8554 86.8204 33.4103 87.8628 30.7038 89.9477C28.0013 92.0365 26.42 95.6005 25.9833 100.675H5.03605C6.27125 80.8646 19.3313 70.9555 44.2163 70.9476C68.4247 70.9476 80.5249 79.6727 80.5171 97.1229V143.561C80.5171 151.224 81.6972 156.731 84.0574 160.083L62.7915 160.047ZM53.6691 141.425C57.8389 137.766 59.9199 132.503 59.912 125.635V117.704C57.894 119.51 54.566 120.69 49.9517 121.245L38.4691 122.543C32.8045 123.227 28.792 124.553 26.4318 126.52C25.2054 127.563 24.2386 128.876 23.6077 130.357C22.9769 131.838 22.6993 133.446 22.797 135.052C22.7027 136.7 22.9938 138.348 23.6473 139.864C24.3008 141.379 25.2986 142.722 26.5616 143.785C29.1028 145.862 32.7337 146.901 37.4542 146.901C44.1022 146.877 49.5072 145.052 53.6691 141.425Z" fill="#ffffff"/>
|
||||
<path d="M186.056 83.2092C193.042 91.5409 196.531 102.63 196.524 116.477C196.516 130.324 193.026 141.338 186.056 149.521C178.975 157.852 169.463 162.018 157.52 162.018C145.577 162.018 136.569 157.573 130.495 148.683V193.091H109.253V72.9067H129.681V85.4042C135.975 75.7272 145.263 70.8847 157.544 70.8768C169.463 70.8768 178.967 74.9876 186.056 83.2092V83.2092ZM152.458 144.505C159.428 144.505 164.935 141.972 168.979 136.905C173.023 131.838 174.99 124.805 174.88 115.804C174.88 106.906 172.937 100.12 169.05 95.4471C165.164 90.7738 159.621 88.4371 152.422 88.4371C145.341 88.4371 139.834 90.8603 135.9 95.7067C131.967 100.553 130 107.477 130 116.477C130 125.588 132.026 132.511 136.077 137.247C139.901 142.086 145.361 144.505 152.458 144.505Z" fill="#ffffff"/>
|
||||
<path d="M295.501 83.2092C302.487 91.5409 305.977 102.63 305.969 116.477C305.961 130.324 302.472 141.338 295.501 149.521C288.42 157.852 278.908 162.018 266.965 162.018C255.023 162.018 246.014 157.573 239.941 148.683V193.091H218.698V72.9067H239.138V85.4042C245.432 75.7272 254.72 70.8847 267.001 70.8768C278.873 70.8768 288.373 74.9876 295.501 83.2092ZM261.903 144.505C268.873 144.505 274.381 141.972 278.424 136.905C282.468 131.838 284.435 124.805 284.325 115.804C284.325 106.906 282.382 100.12 278.495 95.4471C274.609 90.7738 269.066 88.4371 261.867 88.4371C254.787 88.4371 249.279 90.8603 245.346 95.7067C241.412 100.553 239.445 107.477 239.445 116.477C239.445 125.588 241.475 132.511 245.534 137.247C249.311 142.086 254.747 144.505 261.844 144.505H261.903Z" fill="#ffffff"/>
|
||||
<path d="M325.158 132.35H345.928C346.494 137.755 348.268 141.582 351.25 143.832C354.232 146.082 358.933 147.208 365.353 147.208C376.839 147.208 382.583 143.718 382.583 136.74C382.644 135.312 382.333 133.892 381.682 132.62C381.03 131.348 380.06 130.267 378.865 129.482C376.387 127.79 371.938 126.382 365.518 125.257L355.558 123.57C336.534 120.423 327.022 111.642 327.022 97.2291C327.022 89.0075 330.169 82.5601 336.463 77.8868C342.757 73.2135 351.651 70.8768 363.146 70.8768C388.023 70.8768 400.859 80.7112 401.653 100.38H381.591C381.363 95.2111 379.731 91.5251 376.694 89.3222C373.657 87.1193 369.153 86.0218 363.181 86.0297C353.048 86.0297 347.981 89.4088 347.981 96.167C347.928 97.4972 348.209 98.8196 348.799 100.013C349.388 101.207 350.268 102.234 351.356 103C353.614 104.628 357.332 105.895 362.509 106.8L373.484 108.488C384.066 110.399 391.748 113.44 396.532 117.61C401.315 121.78 403.707 127.578 403.707 135.005C403.707 143.785 400.332 150.539 393.581 155.268C386.831 159.996 377.315 162.356 365.034 162.349C339.591 162.388 326.298 152.388 325.158 132.35V132.35Z" fill="#ffffff"/>
|
||||
<path d="M549.335 79.3265C554.677 84.9597 557.352 92.6699 557.36 102.457V160.047H536.117V105.832C536.117 94.8177 531.106 89.3104 521.082 89.3104C518.592 89.2151 516.113 89.6885 513.833 90.6945C511.553 91.7006 509.532 93.2129 507.924 95.1167C504.612 98.9954 502.952 104.652 502.944 112.087V160.047H481.867V105.832C481.867 94.8177 476.804 89.3104 466.678 89.3104C464.224 89.2393 461.785 89.7271 459.546 90.7371C457.307 91.7471 455.327 93.2527 453.756 95.1403C450.428 99.0189 448.768 104.676 448.776 112.111V160.071H427.534V72.9067H448.139V85.4042C454.094 75.7272 462.355 70.8847 472.921 70.8768C486.21 70.8768 495.218 76.2818 499.946 87.0918C506.468 76.2818 515.473 70.8768 526.959 70.8768C536.534 70.8768 543.993 73.6934 549.335 79.3265V79.3265Z" fill="#ffffff"/>
|
||||
<path d="M720.347 89.1216H693.204V136.232C693.204 139.041 693.963 141.008 695.482 142.133C697 143.258 699.545 143.809 703.117 143.785H718.66V160C714.045 160.449 702.055 160.673 699.235 160.673C689.794 160.673 682.898 158.899 678.547 155.35C674.196 151.802 672.029 146.145 672.044 138.38V89.1216H652.289V72.9066H672.044V43.8165H693.204V72.9066H720.347V89.1216Z" fill="#ffffff"/>
|
||||
<path d="M815.536 79.0669C821.004 84.5348 823.734 92.2765 823.726 102.292V160.047H802.484V105.832C802.484 94.8177 797.024 89.3104 786.104 89.3104C783.52 89.2569 780.955 89.746 778.572 90.7461C776.19 91.7463 774.044 93.2352 772.273 95.1167C768.512 98.9953 766.628 104.369 766.62 111.237V160.047H745.378V43.8165H766.62V84.5545C773.26 75.4439 782.095 70.8847 793.126 70.8768C802.614 70.8768 810.084 73.6069 815.536 79.0669V79.0669Z" fill="#ffffff"/>
|
||||
<path d="M840.909 188.748V172.533H923.99V188.748H840.909Z" fill="#FF6D2D"/>
|
||||
<path d="M622.101 143.832V72.9066H577.363V89.1216H600.836V143.832H577.363V160.047H645.574V143.832H622.101Z" fill="#ffffff"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1_41">
|
||||
<rect width="924" height="235" fill="black"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user