Compare commits

...

103 Commits

Author SHA1 Message Date
Andras Bacsai
f21c12f39b Merge pull request #2856 from coollabsio/next
v4.0.0-beta.316
2024-07-19 13:45:51 +02:00
Andras Bacsai
6c1e50a914 fix: backup downloads 2024-07-19 13:45:04 +02:00
Andras Bacsai
da064def7a update service-templates 2024-07-19 10:06:26 +02:00
Andras Bacsai
3af3fa5773 refactor: Update DockerCleanupJob to use server settings for force cleanup 2024-07-19 09:59:09 +02:00
Andras Bacsai
005bd55fb2 refactor: Update DockerCleanupJob to use server settings for force cleanup 2024-07-18 15:12:52 +02:00
Andras Bacsai
82a5b4c55d refactor: server status job and docker cleanup job 2024-07-18 14:43:21 +02:00
Andras Bacsai
b8e95b2099 feat: force cleanup server 2024-07-18 14:38:56 +02:00
Andras Bacsai
8ea50dc029 refactor: Update DockerCleanupJob to handle nullable usageBefore property 2024-07-18 14:28:33 +02:00
Andras Bacsai
ec191af874 chore: Handle JSON parsing errors in format_docker_command_output_to_json 2024-07-18 14:23:15 +02:00
Andras Bacsai
d98c742aff chore: update general page of apps 2024-07-18 14:20:22 +02:00
Andras Bacsai
2529496594 feat: preserve git repository 2024-07-18 13:14:07 +02:00
Andras Bacsai
1b6114036a chore: Update checkbox labels in general.blade.php 2024-07-18 12:40:17 +02:00
Andras Bacsai
b33fb6c39a chore: Update width of container in general.blade.php 2024-07-18 12:39:49 +02:00
Andras Bacsai
0a6826af58 remove ray 2024-07-18 12:32:33 +02:00
Andras Bacsai
1c7034ff78 fix: if git limit reached, ignore it and continue with a default selection 2024-07-18 12:30:45 +02:00
Andras Bacsai
7e11698c55 chore: Update repository form with simplified URL input field 2024-07-18 12:13:23 +02:00
Andras Bacsai
1c4eb31d59 fix: handle custom_internal_name check in ApplicationDeploymentJob.php 2024-07-18 12:10:59 +02:00
Andras Bacsai
b4b6a4294a chore: Update bug report template
Update the bug report template to include a checkbox for indicating whether the user is using the cloud version of Coolify.
2024-07-18 12:07:44 +02:00
Andras Bacsai
4c031a7c05 fix: handle / in preselecting branches 2024-07-18 12:03:48 +02:00
Andras Bacsai
997a262b6c Merge pull request #2840 from Pjort/next
Update supabase.yaml
2024-07-18 10:38:36 +02:00
Andras Bacsai
c0e88df3e8 feat: add readonly labels 2024-07-17 14:52:40 +02:00
Andras Bacsai
85e1cbad53 chore: Update version to 4.0.0-beta.316 2024-07-17 09:17:02 +02:00
Andras Bacsai
c37398af72 Merge pull request #2853 from coollabsio/next
v4.0.0-beta.315
2024-07-17 08:45:33 +02:00
Andras Bacsai
19cfe4e514 fix: new docker compose parsing 2024-07-17 08:09:33 +02:00
Andras Bacsai
23a1b1925f fix: tag deployments 2024-07-17 07:59:12 +02:00
Andras Bacsai
1fb8d1e14c revert: pull policy 2024-07-17 07:59:06 +02:00
Andras Bacsai
804c70b575 chore: Update version to 4.0.0-beta.315 2024-07-17 07:58:45 +02:00
Pjort
548c4a4c64 Update supabase.yaml
Fixes problem related to emails sent for invite and forgotten password, that then doesn't actually use the external URL instead uses the hardcoded: http://supabase-kong:8000
2024-07-15 17:47:35 +02:00
Andras Bacsai
2978042162 Merge pull request #2835 from coollabsio/next
v4.0.0-beta.314
2024-07-15 16:42:04 +02:00
Andras Bacsai
4225ec7060 feat: Improve error handling in loadComposeFile method 2024-07-15 16:39:40 +02:00
Andras Bacsai
893339fc8e refactor: Update Docker Compose build command to include --pull flag 2024-07-15 16:39:28 +02:00
Andras Bacsai
356e7b57d2 improvement: add basedir + compose file in new compose based apps 2024-07-15 16:39:22 +02:00
Andras Bacsai
4ee1f1a507 fix: improve github source creation 2024-07-15 15:33:46 +02:00
Andras Bacsai
7d64df60cd fix: drupal 2024-07-15 13:59:33 +02:00
Andras Bacsai
eb3a4ca157 Merge pull request #2463 from emircanerkul/main
Add drupal-with-postgresql service template
2024-07-15 13:51:43 +02:00
Andras Bacsai
a7b5157fa6 fix: docmost template 2024-07-15 12:58:29 +02:00
Andras Bacsai
793e6d19eb Merge pull request #2747 from alfinauzikri/main
[TEMPLATE] Add Docmost Template
2024-07-15 12:54:36 +02:00
Andras Bacsai
674fa4d09c fix: vikunja 2024-07-15 12:51:04 +02:00
Andras Bacsai
0089e86dd1 refactor: Remove unused code and fix storage form layout 2024-07-15 12:23:06 +02:00
Andras Bacsai
e1d802b507 Merge pull request #2817 from luckydonald/patch-2
[TEMPLATE] fix vikunja, add postgres variant.
2024-07-15 12:18:21 +02:00
Andras Bacsai
9927b71af9 fix: plane service template 2024-07-15 12:13:34 +02:00
Andras Bacsai
b1c0f105ab fix: update docker compose pull command with --policy always 2024-07-15 12:13:21 +02:00
Andras Bacsai
35cae1d4dc Merge pull request #2831 from MrAlexand0r/main
[Feature] #2354 - Add Plane Service
2024-07-15 11:48:53 +02:00
Andras Bacsai
3dab3365e2 fix service-templates 2024-07-15 11:40:12 +02:00
Andras Bacsai
1bdc7c87ba Delete templates/service-templates.json 2024-07-15 11:34:40 +02:00
Andras Bacsai
cab8ad0ca0 Merge pull request #2826 from truemiller/patch-1
Fix typo in "Is Literal?" checkbox in Environment Variables
2024-07-15 11:32:08 +02:00
Andras Bacsai
43409f3ff0 fix: add validation for missing docker compose file 2024-07-15 11:31:18 +02:00
Andras Bacsai
a5dd4cab52 fix: update minio hc in services 2024-07-15 11:31:13 +02:00
Andras Bacsai
a815240f4e Merge pull request #2827 from Megumiso/fix-placement-constraints
fix placement constraints were ignored
2024-07-15 11:18:27 +02:00
Andras Bacsai
2a44e7c5bd Merge pull request #2829 from mateusfmello/fix-minio-healthcheck
fix(MinIO): error in healthcheck command
2024-07-15 11:14:55 +02:00
Andras Bacsai
28c7e439b1 fix: service domains and envs are properly updated 2024-07-15 10:55:04 +02:00
Andras Bacsai
4396c786b4 refactor: Update version numbers to 4.0.0-beta.314 2024-07-15 10:54:50 +02:00
Alexander Gratzl
b67bb8595f removed health checks none 2024-07-14 00:36:14 +02:00
Mateus Fernandes
bec47487dd fix(MinIO): new command healthcheck
MinIO container were not available, as they do not contain the CURL or WGET commands, but MinIO has its own verification command:
https://github.com/minio/minio/issues/18389
2024-07-13 12:33:54 -03:00
Mateus Fernandes
b110d0c12b fix(reactive-resume): new healthcheck command for MinIO
MinIO container were not available, as they do not contain the CURL or WGET commands, but MinIO has its own verification command:
https://github.com/minio/minio/issues/18389
2024-07-13 12:33:12 -03:00
Alexander G
ae425475b4 #2354 added healthchecks for the services 2024-07-13 10:41:04 +02:00
Megumiso
dc6aee44b3 changed variable name for better readability 2024-07-13 13:26:51 +09:00
Megumiso
4ffea311e8 placement constraints is now working 2024-07-13 13:15:17 +09:00
Alexander
77a6a6e46a Merge branch 'main' of https://github.com/coollabsio/coolify
# Conflicts:
#	templates/service-templates.json
2024-07-13 01:08:17 +02:00
Alexander
2278ba31e7 #2354 WIP plane feature 2024-07-13 00:38:41 +02:00
Josh Miller
aaeec3d340 fix: env is_literal helper text typo 2024-07-12 19:00:20 +01:00
Josh Miller
2cbe530b7e fix: typo in is_literal helper 2024-07-12 18:59:06 +01:00
Andras Bacsai
6ada6d145c Merge pull request #2821 from coollabsio/next
v4.0.0-beta.313
2024-07-12 15:46:08 +02:00
Andras Bacsai
0f55e83591 revert: instancesettings 2024-07-12 15:45:36 +02:00
Andras Bacsai
4017ea7b65 Merge pull request #2819 from coollabsio/next
v4.0.0-beta.312
2024-07-12 15:06:15 +02:00
Andras Bacsai
a85066c644 fix: disable sentinel until a few bugs are fixed 2024-07-12 15:05:12 +02:00
Andras Bacsai
b08d38f339 refactor: Update version numbers to 4.0.0-beta.312 2024-07-12 14:54:54 +02:00
Andras Bacsai
d4f4632461 Merge pull request #2812 from coollabsio/next
v4.0.0-beta.311
2024-07-12 14:15:15 +02:00
Andras Bacsai
666aa041f4 refactor: Update metrics.blade.php to improve alert message clarity 2024-07-12 14:12:44 +02:00
Andras Bacsai
1c565fd502 refactor: Add lazy loading to tags in Livewire configuration view 2024-07-12 14:00:39 +02:00
Luckydonald
7de2b8cbd7 Create vikunja-with-postgres.yaml
to have a db variant.
2024-07-12 13:58:14 +02:00
Luckydonald
852e906736 Update vikunja.yaml, follow recommended docker-compose. 2024-07-12 13:55:37 +02:00
Andras Bacsai
5778466947 refactor: Update Webhooks.php to use nullable type for webhook URLs 2024-07-12 13:54:12 +02:00
Andras Bacsai
7006239b0d refactor: Update Livewire configuration views 2024-07-12 13:40:11 +02:00
Andras Bacsai
49d011574d refactor: Remove unnecessary code in AppServiceProvider.php 2024-07-12 13:34:48 +02:00
Andras Bacsai
046a358ae0 refactor: Update Dockerfile to set CI environment variable to true 2024-07-12 13:02:37 +02:00
Andras Bacsai
d23f5af957 hmmm 2024-07-12 12:59:53 +02:00
Andras Bacsai
20a3f4b200 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-07-12 12:53:09 +02:00
Andras Bacsai
73acda833e feat: Enable legacy model binding in Livewire configuration 2024-07-12 12:53:07 +02:00
andrasbacsai
fa895db76e Fix styling 2024-07-12 10:53:07 +00:00
Andras Bacsai
88f33be5b6 refactor: only get instanceSettings once from db 2024-07-12 12:51:55 +02:00
Andras Bacsai
21612cccf7 refactor: tags view 2024-07-12 12:51:13 +02:00
Andras Bacsai
39a7332343 refactored: webhooks view 2024-07-12 11:52:32 +02:00
Andras Bacsai
21825876fb fix: service status changed event 2024-07-12 11:27:08 +02:00
Andras Bacsai
aaee887d3e fix: respect top-level configs and secrets 2024-07-12 11:21:22 +02:00
Andras Bacsai
cb44373eff chore: Bump version to 4.0.0-beta.311 2024-07-12 11:20:44 +02:00
Andras Bacsai
4e6ea4f584 Merge pull request #2809 from coollabsio/next
Another hoopsy
2024-07-12 10:39:05 +02:00
Andras Bacsai
62a93d3e51 feat: Add new logo for Latitude 2024-07-12 10:38:16 +02:00
Andras Bacsai
f60c281e80 Merge pull request #2808 from coollabsio/next
Forgot to commit, oopsy
2024-07-12 10:36:37 +02:00
Andras Bacsai
43c40cdb09 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-07-12 10:35:52 +02:00
Andras Bacsai
c851262d81 refactor: Reset default labels when docker_compose_domains is modified 2024-07-12 10:35:50 +02:00
Andras Bacsai
91783ccc3e Merge pull request #2805 from coollabsio/next
v4.0.0-beta.310
2024-07-12 10:35:47 +02:00
Andras Bacsai
6ba3d5f86e Merge pull request #2737 from DerrikMilligan/patch-1
fix: Add arch as supported os
2024-07-12 10:00:48 +02:00
Andras Bacsai
a9a20755a9 Merge pull request #2799 from janbiasi/template-twenty-update-env
[TEMPLATE] feat: add security and storage access key env to twenty template
2024-07-12 10:00:29 +02:00
Andras Bacsai
d2693c1ac8 chore: Add new logo for Latitude 2024-07-12 09:39:06 +02:00
Jan Biasi
aaa6f434a9 feat: add security and storage access key env to twenty template 2024-07-12 09:23:51 +02:00
Andras Bacsai
314a3ac83f chore: update composer dependencies 2024-07-12 09:05:31 +02:00
Andras Bacsai
36e177479e chore: update version to 4.0.0-beta.310 2024-07-12 09:05:25 +02:00
Alfin Auzikri
0c40c0d795 Add files via upload 2024-07-04 23:38:52 +07:00
Alfin Auzikri
25f0a8f0b7 Create docmost.yaml 2024-07-04 23:38:16 +07:00
Derrik Milligan
65a618d019 Add arch as supported os
Update `SUPPORTED_OS` to include the id `arch`. The install script supports `arch` but you can't proceed with a server install because `arch` isn't a `SUPPORTED_OS`
2024-07-02 12:02:38 -06:00
Emircan ERKUL
e7e85456ea Drupal svg logo 2024-06-12 06:54:59 +03:00
Emircan ERKUL
440baf6009 Create drupal-with-postgresql.yaml 2024-06-12 06:50:50 +03:00
84 changed files with 1344 additions and 548 deletions

View File

@@ -1,6 +1,6 @@
name: Bug report
description: 'Create a new bug report.'
title: '[Bug]: '
description: "Create a new bug report."
title: "[Bug]: "
body:
- type: markdown
attributes:
@@ -35,3 +35,12 @@ body:
description: Coolify's version (see top of your screen).
validations:
required: true
- type: checkboxes
attributes:
label: Cloud?
description: "Are you using the cloud version of Coolify?"
options:
- label: Yes
required: false
- label: No
required: false

View File

@@ -2,8 +2,6 @@
)
[![Bounty Issues](https://img.shields.io/static/v1?labelColor=grey&color=6366f1&label=Algora&message=%F0%9F%92%8E+Bounty+issues&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties/new)
[![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcoollabsio%2Fbounties%3Fstatus%3Dopen&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties?status=open)
[![Rewarded Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcoollabsio%2Fbounties%3Fstatus%3Dcompleted&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties?status=completed)
# About the Project
@@ -49,6 +47,7 @@ Special thanks to our biggest sponsors!
<a href="https://coolify.ad.vin/?ref=coolify.io" target="_blank"><img src="./other/logos/advin.png" alt="advin logo" width="250"/></a>
<a href="https://trieve.ai/?ref=coolify.io" target="_blank"><img src="./other/logos/trieve_bg.png" alt="trieve logo" width="180"/></a>
<a href="https://blacksmith.sh/?ref=coolify.io" target="_blank"><img src="./other/logos/blacksmith.svg" alt="blacksmith logo" width="200"/></a>
<a href="https://latitude.sh/?ref=coolify.io" target="_blank"><img src="./other/logos/latitude.svg" alt="latitude logo" width="200"/></a>
## Github Sponsors ($40+)
<a href="https://serpapi.com/?ref=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>

View File

@@ -2,6 +2,7 @@
namespace App\Console\Commands;
use App\Actions\Server\StopSentinel;
use App\Enums\ApplicationDeploymentStatus;
use App\Jobs\CleanupHelperContainersJob;
use App\Models\ApplicationDeploymentQueue;
@@ -23,6 +24,16 @@ class Init extends Command
{
$this->alive();
get_public_ips();
if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
$servers = Server::all();
foreach ($servers as $server) {
$server->settings->update(['is_metrics_enabled' => false]);
if ($server->isFunctional()) {
StopSentinel::dispatch($server);
}
}
}
$full_cleanup = $this->option('full-cleanup');
$cleanup_deployments = $this->option('cleanup-deployments');

View File

@@ -6,6 +6,7 @@ use App\Jobs\CheckLogDrainContainerJob;
use App\Jobs\CleanupInstanceStuffsJob;
use App\Jobs\ContainerStatusJob;
use App\Jobs\DatabaseBackupJob;
use App\Jobs\DockerCleanupJob;
use App\Jobs\PullCoolifyImageJob;
use App\Jobs\PullHelperImageJob;
use App\Jobs\PullSentinelImageJob;
@@ -87,6 +88,7 @@ class Kernel extends ConsoleKernel
}
foreach ($servers as $server) {
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
}
}

View File

@@ -27,7 +27,7 @@ class ServiceStatusChanged implements ShouldBroadcast
public function broadcastOn(): ?array
{
if ($this->userId) {
if (! is_null($this->userId)) {
return [
new PrivateChannel("user.{$this->userId}"),
];

View File

@@ -65,7 +65,7 @@ class Handler extends ExceptionHandler
if ($e instanceof RuntimeException) {
return;
}
$this->settings = InstanceSettings::get();
$this->settings = \App\Models\InstanceSettings::get();
if ($this->settings->do_not_track) {
return;
}

View File

@@ -732,8 +732,10 @@ class ApplicationsController extends Controller
$application->environment_id = $environment->id;
$application->save();
$application->refresh();
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
if (! $application->settings->is_container_label_readonly_enabled) {
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
}
$application->isConfigurationChanged(true);
if ($instantDeploy) {
@@ -826,8 +828,10 @@ class ApplicationsController extends Controller
$application->source_id = $githubApp->id;
$application->save();
$application->refresh();
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
if (! $application->settings->is_container_label_readonly_enabled) {
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
}
$application->isConfigurationChanged(true);
if ($instantDeploy) {
@@ -916,8 +920,10 @@ class ApplicationsController extends Controller
$application->environment_id = $environment->id;
$application->save();
$application->refresh();
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
if (! $application->settings->is_container_label_readonly_enabled) {
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
}
$application->isConfigurationChanged(true);
if ($instantDeploy) {
@@ -996,8 +1002,10 @@ class ApplicationsController extends Controller
$application->git_branch = 'main';
$application->save();
$application->refresh();
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
if (! $application->settings->is_container_label_readonly_enabled) {
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
}
$application->isConfigurationChanged(true);
if ($instantDeploy) {
@@ -1052,8 +1060,10 @@ class ApplicationsController extends Controller
$application->git_branch = 'main';
$application->save();
$application->refresh();
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
if (! $application->settings->is_container_label_readonly_enabled) {
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
}
$application->isConfigurationChanged(true);
if ($instantDeploy) {
@@ -1494,8 +1504,10 @@ class ApplicationsController extends Controller
$fqdn = str($fqdn)->replaceEnd(',', '')->trim();
$fqdn = str($fqdn)->replaceStart(',', '')->trim();
$application->fqdn = $fqdn;
$customLabels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->custom_labels = base64_encode($customLabels);
if (! $application->settings->is_container_label_readonly_enabled) {
$customLabels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->custom_labels = base64_encode($customLabels);
}
$request->offsetUnset('domains');
}

View File

@@ -3,7 +3,6 @@
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\InstanceSettings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use OpenApi\Attributes as OA;
@@ -85,7 +84,7 @@ class OtherController extends Controller
if ($teamId !== '0') {
return response()->json(['message' => 'You are not allowed to enable the API.'], 403);
}
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$settings->update(['is_api_enabled' => true]);
return response()->json(['message' => 'API enabled.'], 200);
@@ -136,7 +135,7 @@ class OtherController extends Controller
if ($teamId !== '0') {
return response()->json(['message' => 'You are not allowed to disable the API.'], 403);
}
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$settings->update(['is_api_enabled' => false]);
return response()->json(['message' => 'API disabled.'], 200);

View File

@@ -4,7 +4,6 @@ namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Application;
use App\Models\InstanceSettings;
use App\Models\Project;
use App\Models\Server as ModelsServer;
use Illuminate\Http\Request;
@@ -301,7 +300,7 @@ class ServersController extends Controller
$projects = Project::where('team_id', $teamId)->get();
$domains = collect();
$applications = $projects->pluck('applications')->flatten();
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if ($applications->count() > 0) {
foreach ($applications as $application) {
$ip = $application->destination->server->ip;

View File

@@ -2,7 +2,6 @@
namespace App\Http\Middleware;
use App\Models\InstanceSettings;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -15,7 +14,7 @@ class ApiAllowed
if (isCloud()) {
return $next($request);
}
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if ($settings->is_api_enabled === false) {
return response()->json(['success' => true, 'message' => 'API is disabled.'], 403);
}

View File

@@ -157,6 +157,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private ?string $coolify_variables = null;
private bool $preserveRepository = true;
public $tries = 1;
public function __construct(int $application_deployment_queue_id)
@@ -187,6 +189,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->server = $this->mainServer = $this->destination->server;
$this->serverUser = $this->server->user;
$this->is_this_additional_server = $this->application->additional_servers()->wherePivot('server_id', $this->server->id)->count() > 0;
$this->preserveRepository = $this->application->settings->is_preserve_repository_enabled;
$this->basedir = $this->application->generateBaseDir($this->deployment_uuid);
$this->workdir = "{$this->basedir}".rtrim($this->application->base_directory, '/');
@@ -296,13 +299,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
} else {
$this->write_deployment_configurations();
}
$this->execute_remote_command(
[
"docker rm -f {$this->deployment_uuid} >/dev/null 2>&1",
'hidden' => true,
'ignore_errors' => true,
]
);
// $this->execute_remote_command(
// [
// "docker rm -f {$this->deployment_uuid} >/dev/null 2>&1",
// 'hidden' => true,
// 'ignore_errors' => true,
// ]
// );
// $this->execute_remote_command(
// [
@@ -462,7 +465,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if ($this->env_filename) {
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
}
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build";
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build --pull";
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, $command), 'hidden' => true],
);
@@ -517,6 +520,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
}
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
ray($command);
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, $command), 'hidden' => true],
);
@@ -605,6 +610,28 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private function write_deployment_configurations()
{
if ($this->preserveRepository) {
if ($this->use_build_server) {
$this->server = $this->original_server;
}
if (str($this->configuration_dir)->isNotEmpty()) {
ray("docker cp {$this->deployment_uuid}:{$this->workdir} {$this->configuration_dir}");
$this->execute_remote_command(
[
"mkdir -p $this->configuration_dir",
],
[
"rm -rf $this->configuration_dir/{*,.*}",
],
[
"docker cp {$this->deployment_uuid}:{$this->workdir}/. {$this->configuration_dir}",
],
);
}
if ($this->use_build_server) {
$this->server = $this->build_server;
}
}
if (isset($this->docker_compose_base64)) {
if ($this->use_build_server) {
$this->server = $this->original_server;
@@ -1007,7 +1034,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
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.');
}
if (isset($this->application->settings->custom_internal_name)) {
if (str($this->application->settings->custom_internal_name)->isNotEmpty()) {
$this->application_deployment_queue->addLogEntry('Custom internal name is set, rolling update is not supported.');
}
if ($this->pull_request_id !== 0) {
@@ -1523,7 +1550,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->application->custom_labels = base64_encode($labels->implode("\n"));
$this->application->save();
} else {
$labels = collect(generateLabelsApplication($this->application, $this->preview));
if (! $this->application->settings->is_container_label_readonly_enabled) {
$labels = collect(generateLabelsApplication($this->application, $this->preview));
}
}
if ($this->pull_request_id !== 0) {
$labels = collect(generateLabelsApplication($this->application, $this->preview));
@@ -1624,12 +1653,15 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
],
],
];
if (data_get($this->application, 'swarm_placement_constraints')) {
$swarm_placement_constraints = Yaml::parse(base64_decode(data_get($this->application, 'swarm_placement_constraints')));
$docker_compose['services'][$this->container_name]['deploy'] = array_merge(
$docker_compose['services'][$this->container_name]['deploy'],
$swarm_placement_constraints
);
}
if (data_get($this->application, 'settings.is_swarm_only_worker_nodes')) {
$docker_compose['services'][$this->container_name]['deploy']['placement'] = [
'constraints' => [
'node.role == worker',
],
];
$docker_compose['services'][$this->container_name]['deploy']['placement']['constraints'][] = 'node.role == worker';
}
if ($this->pull_request_id !== 0) {
$docker_compose['services'][$this->container_name]['deploy']['replicas'] = 1;
@@ -2028,23 +2060,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
}
}
private function build_by_compose_file()
{
$this->application_deployment_queue->addLogEntry('Pulling & building required images.');
if ($this->application->build_pack === 'dockerimage') {
$this->application_deployment_queue->addLogEntry('Pulling latest images from the registry.');
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} pull"), 'hidden' => true],
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} build"), 'hidden' => true],
);
} else {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), 'hidden' => true],
);
}
$this->application_deployment_queue->addLogEntry('New images built.');
}
private function start_by_compose_file()
{
if ($this->application->build_pack === 'dockerimage') {

View File

@@ -12,7 +12,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use RuntimeException;
class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
{
@@ -20,47 +19,48 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
public $timeout = 300;
public ?int $usageBefore = null;
public int|string|null $usageBefore = null;
public function __construct(public Server $server) {}
public function handle(): void
{
try {
$isInprogress = false;
$this->server->applications()->each(function ($application) use (&$isInprogress) {
if ($application->isDeploymentInprogress()) {
$isInprogress = true;
// $isInprogress = false;
// $this->server->applications()->each(function ($application) use (&$isInprogress) {
// if ($application->isDeploymentInprogress()) {
// $isInprogress = true;
return;
}
});
// return;
// }
// });
// if ($isInprogress) {
// throw new RuntimeException('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...');
// }
if (! $this->server->isFunctional()) {
return;
}
if ($this->server->settings->is_force_cleanup_enabled) {
Log::info('DockerCleanupJob force cleanup on '.$this->server->name);
CleanupDocker::run(server: $this->server, force: true);
return;
}
$this->usageBefore = $this->server->getDiskUsage();
ray('Usage before: '.$this->usageBefore);
if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) {
ray('Cleaning up '.$this->server->name);
CleanupDocker::run($this->server);
CleanupDocker::run(server: $this->server, force: false);
$usageAfter = $this->server->getDiskUsage();
if ($usageAfter < $this->usageBefore) {
$this->server->team?->notify(new DockerCleanup($this->server, 'Saved '.($this->usageBefore - $usageAfter).'% disk space.'));
// ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
// send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
Log::info('DockerCleanupJob done: Saved '.($this->usageBefore - $usageAfter).'% disk space on '.$this->server->name);
} else {
Log::info('DockerCleanupJob failed to save disk space on '.$this->server->name);
}
} else {
ray('No need to clean up '.$this->server->name);
Log::info('No need to clean up '.$this->server->name);
}
} catch (\Throwable $e) {
// send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
ray($e->getMessage());
throw $e;
}

View File

@@ -2,7 +2,6 @@
namespace App\Jobs;
use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
@@ -36,7 +35,7 @@ class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue
$latest_version = get_latest_version_of_coolify();
instant_remote_process(["docker pull -q ghcr.io/coollabsio/coolify:{$latest_version}"], $server, false);
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$current_version = config('version');
if (! $settings->is_auto_update_enabled) {
return;

View File

@@ -3,7 +3,6 @@
namespace App\Jobs;
use App\Models\Server;
use App\Notifications\Server\HighDiskUsage;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -44,7 +43,6 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
}
try {
if ($this->server->isFunctional()) {
$this->cleanup(notify: false);
$this->remove_unnecessary_coolify_yaml();
if ($this->server->isSentinelEnabled()) {
$this->server->checkSentinel();
@@ -56,45 +54,7 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
return handleError($e);
}
try {
// $this->check_docker_engine();
} catch (\Throwable $e) {
// Do nothing
}
}
private function check_docker_engine()
{
$version = instant_remote_process([
'docker info',
], $this->server, false);
if (is_null($version)) {
$os = instant_remote_process([
'cat /etc/os-release | grep ^ID=',
], $this->server, false);
$os = str($os)->after('ID=')->trim();
if ($os === 'ubuntu') {
try {
instant_remote_process([
'systemctl start docker',
], $this->server);
} catch (\Throwable $e) {
ray($e->getMessage());
return handleError($e);
}
} else {
try {
instant_remote_process([
'service docker start',
], $this->server);
} catch (\Throwable $e) {
ray($e->getMessage());
return handleError($e);
}
}
}
}
private function remove_unnecessary_coolify_yaml()
@@ -108,28 +68,4 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
], $this->server, false);
}
}
public function cleanup(bool $notify = false): void
{
$this->disk_usage = $this->server->getDiskUsage();
if ($this->disk_usage >= $this->server->settings->cleanup_after_percentage) {
if ($notify) {
if ($this->server->high_disk_usage_notification_sent) {
ray('high disk usage notification already sent');
return;
} else {
$this->server->high_disk_usage_notification_sent = true;
$this->server->save();
$this->server->team?->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage));
}
} else {
DockerCleanupJob::dispatchSync($this->server);
$this->cleanup(notify: true);
}
} else {
$this->server->high_disk_usage_notification_sent = false;
$this->server->save();
}
}
}

View File

@@ -2,7 +2,6 @@
namespace App\Livewire;
use App\Models\InstanceSettings;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Http;
@@ -48,7 +47,7 @@ class Help extends Component
]
);
$mail->subject("[HELP]: {$this->subject}");
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$type = set_transanctional_email_settings($settings);
if (! $type) {
$url = 'https://app.coolify.io/api/feedback';

View File

@@ -2,7 +2,6 @@
namespace App\Livewire\Notifications;
use App\Models\InstanceSettings;
use App\Models\Team;
use App\Notifications\Test;
use Livewire\Component;
@@ -173,7 +172,7 @@ class Email extends Component
public function copyFromInstanceSettings()
{
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if ($settings->smtp_enabled) {
$team = currentTeam();
$team->update([

View File

@@ -84,6 +84,8 @@ class General extends Component
'application.settings.is_static' => 'boolean|required',
'application.settings.is_build_server_enabled' => 'boolean|required',
'application.settings.is_container_label_escape_enabled' => 'boolean|required',
'application.settings.is_container_label_readonly_enabled' => 'boolean|required',
'application.settings.is_preserve_repository_enabled' => 'boolean|required',
'application.watch_paths' => 'nullable',
'application.redirect' => 'string|required',
];
@@ -119,6 +121,8 @@ class General extends Component
'application.settings.is_static' => 'Is static',
'application.settings.is_build_server_enabled' => 'Is build server enabled',
'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled',
'application.settings.is_container_label_readonly_enabled' => 'Is container label readonly',
'application.settings.is_preserve_repository_enabled' => 'Is preserve repository enabled',
'application.watch_paths' => 'Watch paths',
'application.redirect' => 'Redirect',
];
@@ -143,7 +147,7 @@ class General extends Component
$this->ports_exposes = $this->application->ports_exposes;
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
$this->customLabels = $this->application->parseContainerLabels();
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE' && ! $this->application->settings->is_container_label_readonly_enabled) {
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
@@ -290,6 +294,9 @@ class General extends Component
public function resetDefaultLabels()
{
if ($this->application->settings->is_container_label_readonly_enabled) {
return;
}
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
$this->ports_exposes = $this->application->ports_exposes;
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
@@ -350,8 +357,7 @@ class General extends Component
$this->checkFqdns();
$this->application->save();
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE' && ! $this->application->settings->is_container_label_readonly_enabled) {
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
@@ -364,6 +370,7 @@ class General extends Component
}
}
$this->validate();
if ($this->ports_exposes !== $this->application->ports_exposes || $this->is_container_label_escape_enabled !== $this->application->settings->is_container_label_escape_enabled) {
$this->resetDefaultLabels();
}
@@ -390,6 +397,7 @@ class General extends Component
}
if ($this->application->build_pack === 'dockercompose') {
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
foreach ($this->parsedServiceDomains as $serviceName => $service) {
$domain = data_get($service, 'domain');
if ($domain) {
@@ -399,6 +407,9 @@ class General extends Component
check_domain_usage(resource: $this->application);
}
}
if ($this->application->isDirty('docker_compose_domains')) {
$this->resetDefaultLabels();
}
}
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();

View File

@@ -53,6 +53,12 @@ class GithubPrivateRepository extends Component
public ?string $publish_directory = null;
// In case of docker compose
public ?string $base_directory = null;
public ?string $docker_compose_location = '/docker-compose.yaml';
// End of docker compose
protected int $page = 1;
public $build_pack = 'nixpacks';
@@ -68,6 +74,16 @@ class GithubPrivateRepository extends Component
$this->github_apps = GithubApp::private();
}
public function updatedBaseDirectory()
{
if ($this->base_directory) {
$this->base_directory = rtrim($this->base_directory, '/');
if (! str($this->base_directory)->startsWith('/')) {
$this->base_directory = '/'.$this->base_directory;
}
}
}
public function updatedBuildPack()
{
if ($this->build_pack === 'nixpacks') {
@@ -184,6 +200,10 @@ class GithubPrivateRepository extends Component
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
$application->health_check_enabled = false;
}
if ($this->build_pack === 'dockercompose') {
$application['docker_compose_location'] = $this->docker_compose_location;
$application['base_directory'] = $this->base_directory;
}
$fqdn = generateFqdn($destination->server, $application->uuid);
$application->fqdn = $fqdn;

View File

@@ -33,6 +33,12 @@ class GithubPrivateRepositoryDeployKey extends Component
public ?string $publish_directory = null;
// In case of docker compose
public ?string $base_directory = null;
public ?string $docker_compose_location = '/docker-compose.yaml';
// End of docker compose
public string $repository_url;
public string $branch;
@@ -163,6 +169,10 @@ class GithubPrivateRepositoryDeployKey extends Component
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
$application_init['health_check_enabled'] = false;
}
if ($this->build_pack === 'dockercompose') {
$application_init['docker_compose_location'] = $this->docker_compose_location;
$application_init['base_directory'] = $this->base_directory;
}
$application = Application::create($application_init);
$application->settings->is_static = $this->is_static;
$application->settings->save();

View File

@@ -25,14 +25,20 @@ class PublicGitRepository extends Component
public $query;
public bool $branch_found = false;
public bool $branchFound = false;
public string $selected_branch = 'main';
public string $selectedBranch = 'main';
public bool $is_static = false;
public bool $isStatic = false;
public ?string $publish_directory = null;
// In case of docker compose
public ?string $base_directory = null;
public ?string $docker_compose_location = '/docker-compose.yaml';
// End of docker compose
public string $git_branch = 'main';
public int $rate_limit_remaining = 0;
@@ -56,17 +62,21 @@ class PublicGitRepository extends Component
protected $rules = [
'repository_url' => 'required|url',
'port' => 'required|numeric',
'is_static' => 'required|boolean',
'isStatic' => 'required|boolean',
'publish_directory' => 'nullable|string',
'build_pack' => 'required|string',
'base_directory' => 'nullable|string',
'docker_compose_location' => 'nullable|string',
];
protected $validationAttributes = [
'repository_url' => 'repository',
'port' => 'port',
'is_static' => 'static',
'isStatic' => 'static',
'publish_directory' => 'publish directory',
'build_pack' => 'build pack',
'base_directory' => 'base directory',
'docker_compose_location' => 'docker compose location',
];
public function mount()
@@ -79,6 +89,16 @@ class PublicGitRepository extends Component
$this->query = request()->query();
}
public function updatedBaseDirectory()
{
if ($this->base_directory) {
$this->base_directory = rtrim($this->base_directory, '/');
if (! str($this->base_directory)->startsWith('/')) {
$this->base_directory = '/'.$this->base_directory;
}
}
}
public function updatedBuildPack()
{
if ($this->build_pack === 'nixpacks') {
@@ -86,17 +106,17 @@ class PublicGitRepository extends Component
$this->port = 3000;
} elseif ($this->build_pack === 'static') {
$this->show_is_static = false;
$this->is_static = false;
$this->isStatic = false;
$this->port = 80;
} else {
$this->show_is_static = false;
$this->is_static = false;
$this->isStatic = false;
}
}
public function instantSave()
{
if ($this->is_static) {
if ($this->isStatic) {
$this->port = 80;
$this->publish_directory = '/dist';
} else {
@@ -106,12 +126,7 @@ class PublicGitRepository extends Component
$this->dispatch('success', 'Application settings updated!');
}
public function load_any_git()
{
$this->branch_found = true;
}
public function load_branch()
public function loadBranch()
{
try {
if (str($this->repository_url)->startsWith('git@')) {
@@ -135,15 +150,21 @@ class PublicGitRepository extends Component
return handleError($e, $this);
}
try {
$this->branch_found = false;
$this->get_git_source();
$this->get_branch();
$this->selected_branch = $this->git_branch;
$this->branchFound = false;
$this->getGitSource();
$this->getBranch();
$this->selectedBranch = $this->git_branch;
} catch (\Throwable $e) {
if (! $this->branch_found && $this->git_branch == 'main') {
if ($this->rate_limit_remaining == 0) {
$this->selectedBranch = $this->git_branch;
$this->branchFound = true;
return;
}
if (! $this->branchFound && $this->git_branch == 'main') {
try {
$this->git_branch = 'master';
$this->get_branch();
$this->getBranch();
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -153,13 +174,16 @@ class PublicGitRepository extends Component
}
}
private function get_git_source()
private function getGitSource()
{
$this->repository_url_parsed = Url::fromString($this->repository_url);
$this->git_host = $this->repository_url_parsed->getHost();
$this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2);
$this->git_branch = $this->repository_url_parsed->getSegment(4) ?? 'main';
if ($this->repository_url_parsed->getSegment(3) === 'tree') {
$this->git_branch = str($this->repository_url_parsed->getPath())->after('tree/')->value();
} else {
$this->git_branch = 'main';
}
if ($this->git_host == 'github.com') {
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
@@ -169,17 +193,17 @@ class PublicGitRepository extends Component
$this->git_source = 'other';
}
private function get_branch()
private function getBranch()
{
if ($this->git_source === 'other') {
$this->branch_found = true;
$this->branchFound = true;
return;
}
if ($this->git_source->getMorphClass() === 'App\Models\GithubApp') {
['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = githubApi(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}");
$this->rate_limit_reset = Carbon::parse((int) $this->rate_limit_reset)->format('Y-M-d H:i:s');
$this->branch_found = true;
$this->branchFound = true;
}
}
@@ -261,9 +285,13 @@ class PublicGitRepository extends Component
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
$application_init['health_check_enabled'] = false;
}
if ($this->build_pack === 'dockercompose') {
$application_init['docker_compose_location'] = $this->docker_compose_location;
$application_init['base_directory'] = $this->base_directory;
}
$application = Application::create($application_init);
$application->settings->is_static = $this->is_static;
$application->settings->is_static = $this->isStatic;
$application->settings->save();
$fqdn = generateFqdn($destination->server, $application->uuid);

View File

@@ -4,7 +4,6 @@ namespace App\Livewire\Project\Shared\Storages;
use App\Models\LocalPersistentVolume;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
class Show extends Component
{
@@ -12,8 +11,6 @@ class Show extends Component
public bool $isReadOnly = false;
public ?string $modalId = null;
public bool $isFirst = true;
public bool $isService = false;
@@ -32,11 +29,6 @@ class Show extends Component
'host_path' => 'host',
];
public function mount()
{
$this->modalId = new Cuid2(7);
}
public function submit()
{
$this->validate();

View File

@@ -3,32 +3,63 @@
namespace App\Livewire\Project\Shared;
use App\Models\Tag;
use Livewire\Attributes\Validate;
use Livewire\Component;
// Refactored ✅
class Tags extends Component
{
public $resource = null;
public ?string $new_tag = null;
#[Validate('required|string|min:2')]
public string $newTags;
public $tags = [];
protected $listeners = [
'refresh' => '$refresh',
];
protected $rules = [
'resource.tags.*.name' => 'required|string|min:2',
'new_tag' => 'required|string|min:2',
];
protected $validationAttributes = [
'new_tag' => 'tag',
];
public $filteredTags = [];
public function mount()
{
$this->loadTags();
}
public function loadTags()
{
$this->tags = Tag::ownedByCurrentTeam()->get();
$this->filteredTags = $this->tags->filter(function ($tag) {
return ! $this->resource->tags->contains($tag);
});
}
public function submit()
{
try {
$this->validate();
$tags = str($this->newTags)->trim()->explode(' ');
foreach ($tags as $tag) {
if (strlen($tag) < 2) {
$this->dispatch('error', 'Invalid tag.', "Tag <span class='dark:text-warning'>$tag</span> is invalid. Min length is 2.");
continue;
}
if ($this->resource->tags()->where('name', $tag)->exists()) {
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='dark:text-warning'>$tag</span> already added.");
continue;
}
$found = Tag::ownedByCurrentTeam()->where(['name' => $tag])->exists();
if (! $found) {
$found = Tag::create([
'name' => $tag,
'team_id' => currentTeam()->id,
]);
}
$this->resource->tags()->attach($found->id);
}
$this->refresh();
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function addTag(string $id, string $name)
@@ -39,8 +70,9 @@ class Tags extends Component
return;
}
$this->resource->tags()->syncWithoutDetaching($id);
$this->resource->tags()->attach($id);
$this->refresh();
$this->dispatch('success', 'Tag added.');
} catch (\Exception $e) {
return handleError($e, $this);
}
@@ -50,12 +82,12 @@ class Tags extends Component
{
try {
$this->resource->tags()->detach($id);
$found_more_tags = Tag::where(['id' => $id, 'team_id' => currentTeam()->id])->first();
if ($found_more_tags->applications()->count() == 0 && $found_more_tags->services()->count() == 0) {
$found_more_tags = Tag::ownedByCurrentTeam()->find($id);
if ($found_more_tags && $found_more_tags->applications()->count() == 0 && $found_more_tags->services()->count() == 0) {
$found_more_tags->delete();
}
$this->refresh();
$this->dispatch('success', 'Tag deleted.');
} catch (\Exception $e) {
return handleError($e, $this);
}
@@ -63,41 +95,8 @@ class Tags extends Component
public function refresh()
{
$this->resource->load(['tags']);
$this->tags = Tag::ownedByCurrentTeam()->get();
$this->new_tag = null;
}
public function submit()
{
try {
$this->validate([
'new_tag' => 'required|string|min:2',
]);
$tags = str($this->new_tag)->trim()->explode(' ');
foreach ($tags as $tag) {
if ($this->resource->tags()->where('name', $tag)->exists()) {
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='dark:text-warning'>$tag</span> already added.");
continue;
}
$found = Tag::where(['name' => $tag, 'team_id' => currentTeam()->id])->first();
if (! $found) {
$found = Tag::create([
'name' => $tag,
'team_id' => currentTeam()->id,
]);
}
$this->resource->tags()->syncWithoutDetaching($found->id);
}
$this->refresh();
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function render()
{
return view('livewire.project.shared.tags');
$this->resource->refresh(); // Remove this when legacy_model_binding is false
$this->loadTags();
$this->reset('newTags');
}
}

View File

@@ -4,49 +4,61 @@ namespace App\Livewire\Project\Shared;
use Livewire\Component;
// Refactored ✅
class Webhooks extends Component
{
public $resource;
public ?string $deploywebhook = null;
public ?string $deploywebhook;
public ?string $githubManualWebhook = null;
public ?string $githubManualWebhook;
public ?string $gitlabManualWebhook = null;
public ?string $gitlabManualWebhook;
public ?string $bitbucketManualWebhook = null;
public ?string $bitbucketManualWebhook;
public ?string $giteaManualWebhook = null;
public ?string $giteaManualWebhook;
protected $rules = [
'resource.manual_webhook_secret_github' => 'nullable|string',
'resource.manual_webhook_secret_gitlab' => 'nullable|string',
'resource.manual_webhook_secret_bitbucket' => 'nullable|string',
'resource.manual_webhook_secret_gitea' => 'nullable|string',
];
public ?string $githubManualWebhookSecret = null;
public function saveSecret()
public ?string $gitlabManualWebhookSecret = null;
public ?string $bitbucketManualWebhookSecret = null;
public ?string $giteaManualWebhookSecret = null;
public function mount()
{
// ray()->clearAll();
// ray()->showQueries();
$this->deploywebhook = generateDeployWebhook($this->resource);
$this->githubManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_github');
$this->githubManualWebhook = generateGitManualWebhook($this->resource, 'github');
$this->gitlabManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_gitlab');
$this->gitlabManualWebhook = generateGitManualWebhook($this->resource, 'gitlab');
$this->bitbucketManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_bitbucket');
$this->bitbucketManualWebhook = generateGitManualWebhook($this->resource, 'bitbucket');
$this->giteaManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_gitea');
$this->giteaManualWebhook = generateGitManualWebhook($this->resource, 'gitea');
}
public function submit()
{
try {
$this->validate();
$this->resource->save();
$this->authorize('update', $this->resource);
$this->resource->update([
'manual_webhook_secret_github' => $this->githubManualWebhookSecret,
'manual_webhook_secret_gitlab' => $this->gitlabManualWebhookSecret,
'manual_webhook_secret_bitbucket' => $this->bitbucketManualWebhookSecret,
'manual_webhook_secret_gitea' => $this->giteaManualWebhookSecret,
]);
$this->dispatch('success', 'Secret Saved.');
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function mount()
{
$this->deploywebhook = generateDeployWebhook($this->resource);
$this->githubManualWebhook = generateGitManualWebhook($this->resource, 'github');
$this->gitlabManualWebhook = generateGitManualWebhook($this->resource, 'gitlab');
$this->bitbucketManualWebhook = generateGitManualWebhook($this->resource, 'bitbucket');
$this->giteaManualWebhook = generateGitManualWebhook($this->resource, 'gitea');
}
public function render()
{
return view('livewire.project.shared.webhooks');
}
}

View File

@@ -37,6 +37,7 @@ class Form extends Component
'server.settings.is_swarm_manager' => 'required|boolean',
'server.settings.is_swarm_worker' => 'required|boolean',
'server.settings.is_build_server' => 'required|boolean',
'server.settings.is_force_cleanup_enabled' => 'required|boolean',
'server.settings.concurrent_builds' => 'required|integer|min:1',
'server.settings.dynamic_timeout' => 'required|integer|min:1',
'server.settings.is_metrics_enabled' => 'required|boolean',

View File

@@ -18,7 +18,7 @@ class Index extends Component
public function mount()
{
if (isInstanceAdmin()) {
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$database = StandalonePostgresql::whereName('coolify-db')->first();
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
if ($database) {

View File

@@ -29,7 +29,7 @@ class License extends Component
abort(404);
}
$this->instance_id = config('app.id');
$this->settings = InstanceSettings::get();
$this->settings = \App\Models\InstanceSettings::get();
}
public function render()

View File

@@ -4,7 +4,6 @@ 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;
@@ -100,7 +99,7 @@ class Change extends Component
return redirect()->route('source.all');
}
$this->applications = $this->github_app->applications;
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
$this->name = str($this->github_app->name)->kebab();

View File

@@ -23,7 +23,7 @@ class Index extends Component
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
return redirect()->route('subscription.show');
}
$this->settings = InstanceSettings::get();
$this->settings = \App\Models\InstanceSettings::get();
$this->alreadySubscribed = currentTeam()->subscription()->exists();
}

View File

@@ -2,7 +2,7 @@
namespace App\Livewire\Tags;
use App\Http\Controllers\Api\Deploy;
use App\Http\Controllers\Api\DeployController;
use App\Models\Tag;
use Illuminate\Support\Collection;
use Livewire\Attributes\Url;
@@ -51,11 +51,11 @@ class Index extends Component
{
try {
$this->applications->each(function ($resource) {
$deploy = new Deploy();
$deploy = new DeployController();
$deploy->deploy_resource($resource);
});
$this->services->each(function ($resource) {
$deploy = new Deploy();
$deploy = new DeployController();
$deploy->deploy_resource($resource);
});
$this->dispatch('success', 'Mass deployment started.');

View File

@@ -2,7 +2,7 @@
namespace App\Livewire\Tags;
use App\Http\Controllers\Api\Deploy;
use App\Http\Controllers\Api\DeployController;
use App\Models\ApplicationDeploymentQueue;
use App\Models\Tag;
use Livewire\Component;
@@ -59,11 +59,11 @@ class Show extends Component
try {
$message = collect([]);
$this->applications->each(function ($resource) use ($message) {
$deploy = new Deploy();
$deploy = new DeployController();
$message->push($deploy->deploy_resource($resource));
});
$this->services->each(function ($resource) use ($message) {
$deploy = new Deploy();
$deploy = new DeployController();
$message->push($deploy->deploy_resource($resource));
});
$this->dispatch('success', 'Mass deployment started.');

View File

@@ -1081,45 +1081,55 @@ class Application extends BaseModel
'git read-tree -mu HEAD',
"cat .$workdir$composeFile",
]);
$composeFileContent = instant_remote_process($commands, $this->destination->server, false);
if (! $composeFileContent) {
try {
$composeFileContent = instant_remote_process($commands, $this->destination->server);
} catch (\Exception $e) {
if (str($e->getMessage())->contains('No such file')) {
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
}
if (str($e->getMessage())->contains('fatal: repository') && str($e->getMessage())->contains('does not exist')) {
if ($this->deploymentType() === 'deploy_key') {
throw new \RuntimeException('Your deploy key does not have access to the repository. Please check your deploy key and try again.');
}
throw new \RuntimeException('Repository does not exist. Please check your repository URL and try again.');
}
throw new \RuntimeException($e->getMessage());
} finally {
$this->docker_compose_location = $initialDockerComposeLocation;
$this->save();
$commands = collect([
"rm -rf /tmp/{$uuid}",
]);
instant_remote_process($commands, $this->destination->server, false);
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
} else {
}
if ($composeFileContent) {
$this->docker_compose_raw = $composeFileContent;
$this->save();
}
$commands = collect([
"rm -rf /tmp/{$uuid}",
]);
instant_remote_process($commands, $this->destination->server, false);
$parsedServices = $this->parseCompose();
if ($this->docker_compose_domains) {
$json = collect(json_decode($this->docker_compose_domains));
$names = collect(data_get($parsedServices, 'services'))->keys()->toArray();
$jsonNames = $json->keys()->toArray();
$diff = array_diff($jsonNames, $names);
$json = $json->filter(function ($value, $key) use ($diff) {
return ! in_array($key, $diff);
});
if ($json) {
$this->docker_compose_domains = json_encode($json);
} else {
$this->docker_compose_domains = null;
$parsedServices = $this->parseCompose();
if ($this->docker_compose_domains) {
$json = collect(json_decode($this->docker_compose_domains));
$names = collect(data_get($parsedServices, 'services'))->keys()->toArray();
$jsonNames = $json->keys()->toArray();
$diff = array_diff($jsonNames, $names);
$json = $json->filter(function ($value, $key) use ($diff) {
return ! in_array($key, $diff);
});
if ($json) {
$this->docker_compose_domains = json_encode($json);
} else {
$this->docker_compose_domains = null;
}
$this->save();
}
$this->save();
return [
'parsedServices' => $parsedServices,
'initialDockerComposeLocation' => $this->docker_compose_location,
];
} else {
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
}
return [
'parsedServices' => $parsedServices,
'initialDockerComposeLocation' => $this->docker_compose_location,
];
}
public function parseContainerLabels(?ApplicationPreview $preview = null)

View File

@@ -318,7 +318,7 @@ respond 404
public function setupDynamicProxyConfiguration()
{
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$dynamic_config_path = $this->proxyPath().'/dynamic';
if ($this->proxyType() === 'TRAEFIK_V2') {
$file = "$dynamic_config_path/coolify.yaml";

View File

@@ -24,6 +24,7 @@ use Symfony\Component\Yaml\Yaml;
'destination_id' => ['type' => 'integer', 'description' => 'The unique identifier of the destination where the service is running.'],
'connect_to_docker_network' => ['type' => 'boolean', 'description' => 'The flag to connect the service to the predefined Docker network.'],
'is_container_label_escape_enabled' => ['type' => 'boolean', 'description' => 'The flag to enable the container label escape.'],
'is_container_label_readonly_enabled' => ['type' => 'boolean', 'description' => 'The flag to enable the container label readonly.'],
'config_hash' => ['type' => 'string', 'description' => 'The hash of the service configuration.'],
'service_type' => ['type' => 'string', 'description' => 'The type of the service.'],
'created_at' => ['type' => 'string', 'description' => 'The date and time when the service was created.'],

View File

@@ -2,7 +2,6 @@
namespace App\Notifications\Channels;
use App\Models\InstanceSettings;
use App\Models\User;
use Exception;
use Illuminate\Mail\Message;
@@ -14,7 +13,7 @@ class TransactionalEmailChannel
{
public function send(User $notifiable, Notification $notification): void
{
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if (! data_get($settings, 'smtp_enabled') && ! data_get($settings, 'resend_enabled')) {
Log::info('SMTP/Resend not enabled');

View File

@@ -18,7 +18,7 @@ class ResetPassword extends Notification
public function __construct($token)
{
$this->settings = InstanceSettings::get();
$this->settings = \App\Models\InstanceSettings::get();
$this->token = $token;
}

View File

@@ -2,8 +2,10 @@
namespace App\Providers;
use App\Models\InstanceSettings;
use App\Models\PersonalAccessToken;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Laravel\Sanctum\Sanctum;
@@ -14,6 +16,7 @@ class AppServiceProvider extends ServiceProvider
public function boot(): void
{
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
Http::macro('github', function (string $api_url, ?string $github_access_token = null) {
if ($github_access_token) {
return Http::withHeaders([
@@ -27,5 +30,9 @@ class AppServiceProvider extends ServiceProvider
])->baseUrl($api_url);
}
});
// if (! env('CI')) {
// View::share('instanceSettings', InstanceSettings::get());
// }
}
}

View File

@@ -6,7 +6,6 @@ use App\Actions\Fortify\CreateNewUser;
use App\Actions\Fortify\ResetUserPassword;
use App\Actions\Fortify\UpdateUserPassword;
use App\Actions\Fortify\UpdateUserProfileInformation;
use App\Models\InstanceSettings;
use App\Models\OauthSetting;
use App\Models\User;
use Illuminate\Cache\RateLimiting\Limit;
@@ -45,7 +44,7 @@ class FortifyServiceProvider extends ServiceProvider
{
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::registerView(function () {
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if (! $settings->is_registration_enabled) {
return redirect()->route('login');
}
@@ -57,7 +56,7 @@ class FortifyServiceProvider extends ServiceProvider
});
Fortify::loginView(function () {
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$enabled_oauth_providers = OauthSetting::where('enabled', true)->get();
$users = User::count();
if ($users == 0) {

View File

@@ -40,6 +40,7 @@ const SUPPORTED_OS = [
'ubuntu debian raspbian',
'centos fedora rhel ol rocky amzn almalinux',
'sles opensuse-leap opensuse-tumbleweed',
'arch',
];
const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment'];

View File

@@ -48,9 +48,13 @@ function format_docker_command_output_to_json($rawOutput): Collection
$outputLines = collect($outputLines);
}
return $outputLines
->reject(fn ($line) => empty($line))
->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR));
try {
return $outputLines
->reject(fn ($line) => empty($line))
->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR));
} catch (\Throwable $e) {
return collect([]);
}
}
function format_docker_labels_to_json(string|array $rawOutput): Collection

View File

@@ -88,6 +88,9 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
try {
$name = data_get($resource, 'name');
$dockerComposeRaw = data_get($resource, 'service.docker_compose_raw');
if (! $dockerComposeRaw) {
throw new \Exception('No compose file found or not a valid YAML file.');
}
$dockerCompose = Yaml::parse($dockerComposeRaw);
// Switch Image
@@ -106,7 +109,7 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
if ($resourceFqdns->count() === 1) {
$resourceFqdns = $resourceFqdns->first();
$variableName = 'SERVICE_FQDN_'.str($resource->name)->upper()->replace('-', '');
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'LIKE', "{$variableName}_%")->first();
$fqdn = Url::fromString($resourceFqdns);
$port = $fqdn->getPort();
$path = $fqdn->getPath();
@@ -125,7 +128,7 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
}
}
$variableName = 'SERVICE_URL_'.str($resource->name)->upper()->replace('-', '');
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'LIKE', "{$variableName}_%")->first();
$url = Url::fromString($fqdn);
$port = $url->getPort();
$path = $url->getPath();

View File

@@ -244,13 +244,13 @@ function generate_application_name(string $git_repository, string $git_branch, ?
function is_transactional_emails_active(): bool
{
return isEmailEnabled(InstanceSettings::get());
return isEmailEnabled(\App\Models\InstanceSettings::get());
}
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string
{
if (! $settings) {
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
}
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
@@ -284,7 +284,7 @@ function base_ip(): string
if (isDev()) {
return 'localhost';
}
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if ($settings->public_ipv4) {
return "$settings->public_ipv4";
}
@@ -312,7 +312,7 @@ function getFqdnWithoutPort(string $fqdn)
*/
function base_url(bool $withPort = true): string
{
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if ($settings->fqdn) {
return $settings->fqdn;
}
@@ -379,7 +379,7 @@ function send_internal_notification(string $message): void
}
function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null): void
{
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$type = set_transanctional_email_settings($settings);
if (! $type) {
throw new Exception('No email settings found.');
@@ -774,6 +774,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$allServices = get_service_templates();
$topLevelVolumes = collect(data_get($yaml, 'volumes', []));
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
$topLevelConfigs = collect(data_get($yaml, 'configs', []));
$topLevelSecrets = collect(data_get($yaml, 'secrets', []));
$services = data_get($yaml, 'services');
$generatedServiceFQDNS = collect([]);
@@ -1402,6 +1404,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'services' => $services->toArray(),
'volumes' => $topLevelVolumes->toArray(),
'networks' => $topLevelNetworks->toArray(),
'configs' => $topLevelConfigs->toArray(),
'secrets' => $topLevelSecrets->toArray(),
];
$yaml = data_forget($yaml, 'services.*.volumes.*.content');
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
@@ -1441,6 +1445,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
}
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
$topLevelConfigs = collect(data_get($yaml, 'configs', []));
$topLevelSecrets = collect(data_get($yaml, 'secrets', []));
$services = data_get($yaml, 'services');
$generatedServiceFQDNS = collect([]);
@@ -1682,7 +1688,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$read_only = data_get($volume, 'read_only');
if ($source && $target) {
$uuid = $resource->uuid;
if ((str($source)->startsWith('.') || str($source)->startsWith('~'))) {
if ((str($source)->startsWith('.') || str($source)->startsWith('~') || str($source)->startsWith('/'))) {
$dir = base_configuration_dir().'/applications/'.$resource->uuid;
if (str($source, '.')) {
$source = str($source)->replaceFirst('.', $dir);
@@ -1690,11 +1696,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if (str($source, '~')) {
$source = str($source)->replaceFirst('~', $dir);
}
if ($pull_request_id === 0) {
$source = $uuid."-$source";
} else {
$source = $uuid."-$source-pr-$pull_request_id";
}
if ($read_only) {
data_set($volume, 'source', $source.':'.$target.':ro');
} else {
@@ -2027,14 +2028,20 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
domains: $fqdns,
serviceLabels: $serviceLabels,
generate_unique_uuid: $resource->build_pack === 'dockercompose',
image: data_get($service, 'image')
image: data_get($service, 'image'),
is_force_https_enabled: $resource->isForceHttpsEnabled(),
is_gzip_enabled: $resource->isGzipEnabled(),
is_stripprefix_enabled: $resource->isStripprefixEnabled(),
));
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
network: $resource->destination->network,
uuid: $resource->uuid,
domains: $fqdns,
serviceLabels: $serviceLabels,
image: data_get($service, 'image')
image: data_get($service, 'image'),
is_force_https_enabled: $resource->isForceHttpsEnabled(),
is_gzip_enabled: $resource->isGzipEnabled(),
is_stripprefix_enabled: $resource->isStripprefixEnabled(),
));
}
}
@@ -2080,6 +2087,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'services' => $services->toArray(),
'volumes' => $topLevelVolumes->toArray(),
'networks' => $topLevelNetworks->toArray(),
'configs' => $topLevelConfigs->toArray(),
'secrets' => $topLevelSecrets->toArray(),
];
if ($isSameDockerComposeFile) {
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
@@ -2244,7 +2253,7 @@ function validate_dns_entry(string $fqdn, Server $server)
if (str($host)->contains('sslip.io')) {
return true;
}
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
$is_dns_validation_enabled = data_get($settings, 'is_dns_validation_enabled');
if (! $is_dns_validation_enabled) {
return true;
@@ -2364,7 +2373,7 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId =
if ($domainFound) {
return true;
}
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if (data_get($settings, 'fqdn')) {
$domain = data_get($settings, 'fqdn');
if (str($domain)->endsWith('/')) {
@@ -2381,7 +2390,6 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
if ($resource) {
if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose') {
$domains = data_get(json_decode($resource->docker_compose_domains, true), '*.domain');
ray($domains);
$domains = collect($domains);
} else {
$domains = collect($resource->fqdns);
@@ -2437,7 +2445,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
}
}
if ($resource) {
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if (data_get($settings, 'fqdn')) {
$domain = data_get($settings, 'fqdn');
if (str($domain)->endsWith('/')) {
@@ -2512,7 +2520,7 @@ function get_public_ips()
{
try {
echo "Refreshing public ips!\n";
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
[$first, $second] = Process::concurrently(function (Pool $pool) {
$pool->path(__DIR__)->command('curl -4s https://ifconfig.io');
$pool->path(__DIR__)->command('curl -6s https://ifconfig.io');

View File

@@ -32,6 +32,7 @@
"poliander/cron": "^3.0",
"purplepixie/phpdns": "^2.1",
"pusher/pusher-php-server": "^7.2",
"resend/resend-laravel": "^0.13.0",
"sentry/sentry-laravel": "^4.6",
"socialiteproviders/microsoft-azure": "^5.1",
"spatie/laravel-activitylog": "^4.7.3",
@@ -106,4 +107,4 @@
},
"minimum-stability": "stable",
"prefer-stable": true
}
}

128
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c7c9cc002a9765c2395717c69ba8bfc6",
"content-hash": "cb17445966de6094aef5a92ee59d1d77",
"packages": [
{
"name": "amphp/amp",
@@ -7129,6 +7129,132 @@
],
"time": "2024-07-01T14:24:45+00:00"
},
{
"name": "resend/resend-laravel",
"version": "v0.13.0",
"source": {
"type": "git",
"url": "https://github.com/resend/resend-laravel.git",
"reference": "23aed22df0d0b23c2952da2aaed6a8b88d301a8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/resend/resend-laravel/zipball/23aed22df0d0b23c2952da2aaed6a8b88d301a8a",
"reference": "23aed22df0d0b23c2952da2aaed6a8b88d301a8a",
"shasum": ""
},
"require": {
"illuminate/http": "^10.0|^11.0",
"illuminate/support": "^10.0|^11.0",
"php": "^8.1",
"resend/resend-php": "^0.12.0",
"symfony/mailer": "^6.2|^7.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.14",
"mockery/mockery": "^1.5",
"orchestra/testbench": "^8.17|^9.0",
"pestphp/pest": "^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
},
"laravel": {
"providers": [
"Resend\\Laravel\\ResendServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Resend\\Laravel\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Resend and contributors",
"homepage": "https://github.com/resend/resend-laravel/contributors"
}
],
"description": "Resend for Laravel",
"homepage": "https://resend.com/",
"keywords": [
"api",
"client",
"laravel",
"php",
"resend",
"sdk"
],
"support": {
"issues": "https://github.com/resend/resend-laravel/issues",
"source": "https://github.com/resend/resend-laravel/tree/v0.13.0"
},
"time": "2024-07-08T18:51:42+00:00"
},
{
"name": "resend/resend-php",
"version": "v0.12.0",
"source": {
"type": "git",
"url": "https://github.com/resend/resend-php.git",
"reference": "37fb79bb8160ce2de521bf37484ba59e89236521"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/resend/resend-php/zipball/37fb79bb8160ce2de521bf37484ba59e89236521",
"reference": "37fb79bb8160ce2de521bf37484ba59e89236521",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^7.5",
"php": "^8.1.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.13",
"mockery/mockery": "^1.6",
"pestphp/pest": "^2.0"
},
"type": "library",
"autoload": {
"files": [
"src/Resend.php"
],
"psr-4": {
"Resend\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Resend and contributors",
"homepage": "https://github.com/resend/resend-php/contributors"
}
],
"description": "Resend PHP library.",
"homepage": "https://resend.com/",
"keywords": [
"api",
"client",
"php",
"resend",
"sdk"
],
"support": {
"issues": "https://github.com/resend/resend-php/issues",
"source": "https://github.com/resend/resend-php/tree/v0.12.0"
},
"time": "2024-03-04T03:16:28+00:00"
},
{
"name": "revolt/event-loop",
"version": "v1.0.6",

View File

@@ -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.309',
'release' => '4.0.0-beta.316',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),

View File

@@ -1,3 +1,3 @@
<?php
return '4.0.0-beta.309';
return '4.0.0-beta.316';

View File

@@ -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_container_label_readonly_enabled')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('application_settings', function (Blueprint $table) {
$table->dropColumn('is_container_label_readonly_enabled');
});
}
};

View File

@@ -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_preserve_repository_enabled')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('application_settings', function (Blueprint $table) {
$table->dropColumn('is_preserve_repository_enabled');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('server_settings', function (Blueprint $table) {
$table->boolean('is_force_cleanup_enabled')->default(false)->after('is_sentinel_enabled');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('server_settings', function (Blueprint $table) {
$table->dropColumn('is_force_cleanup_enabled');
});
}
};

View File

@@ -27,14 +27,14 @@ class InstanceSettingsSeeder extends Seeder
$ipv4 = Process::run('curl -4s https://ifconfig.io')->output();
$ipv4 = trim($ipv4);
$ipv4 = filter_var($ipv4, FILTER_VALIDATE_IP);
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if (is_null($settings->public_ipv4) && $ipv4) {
$settings->update(['public_ipv4' => $ipv4]);
}
$ipv6 = Process::run('curl -6s https://ifconfig.io')->output();
$ipv6 = trim($ipv6);
$ipv6 = filter_var($ipv6, FILTER_VALIDATE_IP);
$settings = InstanceSettings::get();
$settings = \App\Models\InstanceSettings::get();
if (is_null($settings->public_ipv6) && $ipv6) {
$settings->update(['public_ipv6' => $ipv6]);
}

View File

@@ -17,6 +17,7 @@ ARG TARGETPLATFORM
# https://github.com/cloudflare/cloudflared/releases
ARG CLOUDFLARED_VERSION=2024.4.1
ARG POSTGRES_VERSION=15
ARG CI=true
WORKDIR /var/www/html

1
other/logos/latitude.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="349" height="64" fill="#ffffff" viewBox="0 0 349 64" ><path fill="#ffffff" d="M76.094 52V7.581h-6.917V52h6.917zM95.64 52.761c3.616 0 7.677-1.776 9.518-4.759V52h6.853V22.43h-6.853v3.997c-1.841-2.982-5.394-4.759-9.328-4.759-9.201 0-14.722 6.854-14.722 15.547 0 8.693 5.013 15.546 14.531 15.546zm1.015-6.155c-5.394 0-8.63-3.997-8.63-9.391s3.236-9.392 8.63-9.392c5.901 0 8.756 4.316 8.756 9.392s-2.855 9.391-8.756 9.391zM132.621 46.162c-2.729 0-4.315-.634-4.315-5.774V28.204h6.79V22.43h-6.79v-8.947h-6.917v8.947h-5.013v5.774h5.013v13.77c0 9.963 6.219 10.407 9.582 10.407 2.031 0 4.061-.19 5.14-.381v-6.092c-.698.127-2.411.254-3.49.254zM144.247 18.178c3.045 0 5.14-2.284 5.14-5.14 0-2.855-2.095-5.14-5.14-5.14-3.046 0-5.14 2.285-5.14 5.14 0 2.856 2.094 5.14 5.14 5.14zM147.673 52V22.43h-6.853V52h6.853zM168.299 46.162c-2.728 0-4.315-.634-4.315-5.774V28.204h6.79V22.43h-6.79v-8.947h-6.916v8.947h-5.013v5.774h5.013v13.77c0 9.963 6.218 10.407 9.581 10.407 2.031 0 4.061-.19 5.14-.381v-6.092c-.698.127-2.411.254-3.49.254zM187.159 52.761c4.251 0 7.361-1.713 9.137-4.886V52h6.917V22.43h-6.917v16.689c0 4.251-1.903 7.741-7.17 7.741-4.505 0-6.028-2.792-6.028-7.551v-16.88h-6.917v18.593c0 7.932 4.505 11.74 10.978 11.74zM232.247 26.427c-1.84-2.982-5.394-4.759-9.328-4.759-9.201 0-14.722 6.854-14.722 15.547 0 8.693 5.013 15.546 14.532 15.546 3.616 0 7.678-1.776 9.518-4.759V52h6.853V7.581h-6.853v18.846zm-8.503 20.18c-5.394 0-8.63-3.998-8.63-9.392 0-5.394 3.236-9.392 8.63-9.392 5.901 0 8.757 4.316 8.757 9.392s-2.856 9.391-8.757 9.391zM273.099 35.692c0-8.313-5.711-14.024-14.15-14.024-9.011 0-14.849 6.092-14.849 15.547 0 9.582 5.774 15.546 15.102 15.546 7.107 0 12.691-3.87 13.58-9.39h-6.98c-1.142 2.283-3.173 3.362-6.6 3.362-5.013 0-8.058-2.538-8.249-7.234h21.956c.127-1.65.19-2.601.19-3.807zm-22.146-1.46c.064-4.188 2.983-7.107 7.869-7.107 4.759 0 7.424 2.539 7.487 7.108h-15.356zM281.59 52.508c2.983 0 5.394-2.475 5.394-5.394s-2.411-5.33-5.394-5.33a5.321 5.321 0 00-5.33 5.33c0 2.919 2.348 5.394 5.33 5.394zM303.931 52.761c7.17 0 11.675-3.426 11.675-9.2 0-4.189-2.665-7.234-6.853-8.44l-5.901-1.713c-2.729-.762-3.681-1.84-3.681-3.427 0-1.777 1.777-3.046 4.252-3.046 3.236 0 5.14 1.46 5.14 3.934h6.789c0-5.71-4.441-9.2-11.675-9.2-6.6 0-11.359 3.68-11.359 8.693 0 4.188 2.348 7.17 6.473 8.312l5.901 1.587c3.173.825 3.871 1.84 3.871 3.49 0 2.094-1.65 3.49-4.252 3.49-3.553 0-6.028-1.777-6.028-4.315h-6.917c0 5.647 5.331 9.835 12.565 9.835zM337.5 21.668c-4.378 0-7.805 1.714-9.645 4.886V7.581h-6.916V52h6.916V35.755c0-5.203 2.538-8.185 7.044-8.185 4.822 0 6.472 2.728 6.472 9.01V52h6.917V33.408c0-7.742-3.681-11.74-10.788-11.74zM50.552 47.654l-5.29 15.762H14.805l5.29-15.762h30.457zM33.744.584H14.863L0 44.875h18.88L33.744.584z" data-darkreader-inline-fill="" style="--darkreader-inline-fill: #ffffff;"></path></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
public/svgs/docmost.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

1
public/svgs/drupal.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="220.03239mm" height="167.50914mm" viewBox="0 0 623.71387 474.82906"><title>Risorsa 28</title><g id="Livello_2" data-name="Livello 2"><g id="Livello_1-2" data-name="Livello 1"><path d="M292.12129,345.00234h-26.212l.04275,49.04508c0,14.33211-6.14488,25.92692-20.477,25.92692-14.33924,0-20.54117-11.59481-20.54117-25.92692V345.04868H198.78953l-.00713,48.99874c0,28.32213,18.20655,51.27636,46.53223,51.27636,28.31849,0,46.79948-22.95423,46.79948-51.27636Z" style="fill:#009cde"/><rect x="528.48024" y="315.11614" width="26.08367" height="127.67755" style="fill:#009cde"/><polygon points="573.823 319.826 573.823 315.239 594.15 315.239 594.15 319.826 586.715 319.826 586.715 340.043 581.261 340.043 581.261 319.826 573.823 319.826" style="fill:#009cde"/><polygon points="604.673 315.24 610.476 332.295 610.544 332.295 616.029 315.24 623.714 315.24 623.714 340.044 618.606 340.044 618.606 322.465 618.535 322.465 612.458 340.044 608.252 340.044 602.175 322.64 602.103 322.64 602.103 340.044 596.999 340.044 596.999 315.24 604.673 315.24" style="fill:#009cde"/><path d="M177.46784,343.93624c-22.48375-5.18962-36.66617,17.148-37.25785,18.34557-.28871.58814-.2994.93035-1.29384.90533-.82337-.01773-.916-.90533-.916-.90533l-2.79086-17.08726H111.84157v97.51614h26.19419v-52.784c0-4.3128,11.61256-24.993,34.11766-19.67153,11.38088,2.69467,16.21054,7.52436,16.21054,7.52436V348.07087a41.85,41.85,0,0,0-10.89612-4.13463" style="fill:#009cde"/><path d="M353.05258,368.64446a26.13539,26.13539,0,1,1-26.13,26.13,26.13748,26.13748,0,0,1,26.13-26.13M327.664,474.82906V439.74191l.00712.00718.00713-13.14169s.03921-1.05141.98729-1.06218c.84474-.01066,1.03368.549,1.24041,1.06218,1.98529,4.94369,12.90641,23.76689,37.14374,17.86435A51.631,51.631,0,1,0,301.402,394.77446v80.0546Z" style="fill:#009cde"/><path d="M492.37655,394.77479a26.13539,26.13539,0,1,1-26.1336-26.13,26.139,26.139,0,0,1,26.1336,26.13m-.74494,47.9793H517.8935v-47.9793a51.62918,51.62918,0,1,0-65.64764,49.69381c24.23739,5.906,35.15845-12.92066,37.14374-17.86087.20673-.5132.39208-1.07284,1.24041-1.06566.94808.01425.98729,1.06566.98729,1.06566" style="fill:#009cde"/><path d="M36.905,337.16373h-10.529v83.26605l10.81417.278c22.18076,0,36.46656-2.01733,36.46656-41.90208,0-38.24519-12.61057-41.642-36.75172-41.642m-7.1108,105.38619H0V315.0649H31.96836c38.70852,0,68.06785,7.10374,68.06785,63.74083,0,56.09518-31.09872,63.74419-70.24206,63.74419" style="fill:#009cde"/><path d="M336.513,60.04433C316.67656,40.21728,297.75071,21.31654,292.11812,0c-5.63286,21.31654-24.56176,40.21728-44.39487,60.04433-29.74983,29.731-63.47995,63.42669-63.47995,113.96434a107.878,107.878,0,1,0,215.756,0c0-50.53435-33.72736-84.23329-63.48628-113.96434M230.09481,199.146c-6.61462-.22461-31.02654-42.30194,14.26152-87.1038l29.96891,32.73593a2.56174,2.56174,0,0,1-.20005,3.82276c-7.15132,7.33453-37.63207,37.90055-41.42061,48.46955-.782,2.18152-1.92407,2.099-2.60977,2.07556m62.02662,55.45668a37.10175,37.10175,0,0,1-37.102-37.102c0-9.39381,3.73446-17.765,9.24757-24.50685,6.68995-8.18054,27.84947-31.18907,27.84947-31.18907s20.83558,23.34627,27.79953,31.111a36.28369,36.28369,0,0,1,9.30744,24.58494,37.10209,37.10209,0,0,1-37.102,37.102m71.01314-60.16628c-.79965,1.74885-2.61363,4.66848-5.062,4.75761-4.36413.15894-4.83045-2.07721-8.05609-6.8511-7.08179-10.47987-68.88406-75.07043-80.44365-87.56214-10.16779-10.987-1.43181-18.733,2.62052-22.79219,5.084-5.09314,19.92417-19.92418,19.92417-19.92418s44.24974,41.984,62.6825,70.67071,12.08027,53.50972,8.33451,61.70129" style="fill:#009cde"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

6
public/svgs/plane.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg width="155" height="155" viewBox="0 0 155 155" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="37" y="64" width="27" height="27" fill="#4075FC"/>
<rect x="91" y="64" width="27" height="27" fill="#4075FC"/>
<rect x="64" y="91" width="27" height="27" fill="#4075FC"/>
<rect x="64" y="36" width="54" height="28" fill="#4075FC"/>
</svg>

After

Width:  |  Height:  |  Size: 346 B

View File

@@ -8,7 +8,7 @@
@use('App\Models\InstanceSettings')
@php
$instanceSettings = InstanceSettings::first();
$instanceSettings = \App\Models\InstanceSettings::get();
$name = null;
if ($instanceSettings) {

View File

@@ -16,12 +16,13 @@
<x-forms.checkbox
helper="Your application will be available only on https if your domain starts with https://..."
instantSave id="is_force_https_enabled" label="Force Https" />
<x-forms.checkbox label="Enable gzip compression"
<x-forms.checkbox label="Enable Gzip Compression"
helper="You can disable gzip compression if you want. Some services are compressing data by default. In this case, you do not need this."
instantSave id="is_gzip_enabled" />
<x-forms.checkbox helper="Strip Prefix is used to remove prefixes from paths. Like /api/ to /api."
instantSave id="is_stripprefix_enabled" label="Strip Prefixes" />
@if ($application->build_pack === 'dockercompose')
<h3>Docker Compose</h3>
<x-forms.checkbox instantSave id="application.settings.is_raw_compose_deployment_enabled"
label="Raw Compose Deployment"
helper="WARNING: Advanced use cases only. Your docker compose file will be deployed as-is. Nothing is modified by Coolify. You need to configure the proxy parts. More info in the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/compose#raw-docker-compose-deployment'>documentation.</a>" />

View File

@@ -109,7 +109,7 @@
<livewire:project.service.storage :resource="$application" />
</div>
<div x-cloak x-show="activeTab === 'webhooks'">
<livewire:project.shared.webhooks :resource="$application" />
<livewire:project.shared.webhooks :resource="$application" lazy />
</div>
<div x-cloak x-show="activeTab === 'previews'">
<livewire:project.application.previews :application="$application" />
@@ -133,7 +133,7 @@
<livewire:project.shared.metrics :resource="$application" />
</div>
<div x-cloak x-show="activeTab === 'tags'">
<livewire:project.shared.tags :resource="$application" />
<livewire:project.shared.tags :resource="$application" lazy />
</div>
<div x-cloak x-show="activeTab === 'danger'">
<livewire:project.shared.danger :resource="$application" />

View File

@@ -30,13 +30,7 @@
</x-forms.select>
@endif
</div>
@if ($application->could_set_build_commands())
<div class="w-64">
<x-forms.checkbox instantSave id="application.settings.is_static"
label="Is it a static site?"
helper="If your application is a static site or the final build assets should be served as a static site, enable this." />
</div>
@endif
@if ($application->build_pack === 'dockercompose')
@if (
!is_null($parsedServices) &&
@@ -57,6 +51,7 @@
@endforeach
@endif
@endif
</div>
@endif
@if ($application->build_pack !== 'dockercompose')
@@ -129,99 +124,152 @@
@endif
</div>
@endif
@if ($application->build_pack !== 'dockerimage')
<h3 class="pt-8">Build</h3>
@if ($application->build_pack !== 'dockercompose')
<div class="max-w-96">
<x-forms.checkbox
helper="Use a build server to build your application. You can configure your build server in the Server settings. This is experimental. For more info, check the <a href='https://coolify.io/docs/knowledge-base/server/build-server' class='underline' target='_blank'>documentation</a>."
instantSave id="application.settings.is_build_server_enabled"
label="Use a Build Server? (experimental)" />
</div>
@endif
@if ($application->could_set_build_commands())
@if ($application->build_pack === 'nixpacks')
<div class="flex flex-col gap-2 xl:flex-row">
<x-forms.input placeholder="If you modify this, you probably need to have a nixpacks.toml"
id="application.install_command" label="Install Command" />
<x-forms.input placeholder="If you modify this, you probably need to have a nixpacks.toml"
id="application.build_command" label="Build Command" />
<x-forms.input placeholder="If you modify this, you probably need to have a nixpacks.toml"
id="application.start_command" label="Start Command" />
</div>
<div class="pb-4 text-xs">Nixpacks will detect the required configuration automatically.
<a class="underline" href="https://coolify.io/docs/resources/introduction">Framework
Specific Docs</a>
</div>
@endif
@endif
@if ($application->build_pack === 'dockercompose')
<div class="flex flex-col gap-2" x-init="$wire.dispatch('loadCompose', true)">
<div class="flex gap-2">
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/"
id="application.base_directory" label="Base Directory"
helper="Directory to use as root. Useful for monorepos." />
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/docker-compose.yaml"
id="application.docker_compose_location" label="Docker Compose Location"
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
</div>
<div class="pt-4">The following commands are for advanced use cases. Only modify them if you
know what are
you doing.</div>
<div class="flex gap-2">
<x-forms.input placeholder="docker compose build" x-bind:disabled="initLoadingCompose"
id="application.docker_compose_custom_build_command"
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} build</span>"
label="Custom Build Command" />
<x-forms.input placeholder="docker compose up -d" x-bind:disabled="initLoadingCompose"
id="application.docker_compose_custom_start_command"
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} up -d</span>"
label="Custom Start Command" />
</div>
</div>
@else
<div class="flex flex-col gap-2 xl:flex-row">
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
helper="Directory to use as root. Useful for monorepos." />
@if ($application->build_pack === 'dockerfile' && !$application->dockerfile)
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
label="Dockerfile Location"
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}</span>" />
@endif
@if ($application->build_pack === 'dockerfile')
<x-forms.input id="application.dockerfile_target_build" label="Docker Build Stage Target"
helper="Useful if you have multi-staged dockerfile." />
@endif
@if ($application->could_set_build_commands())
@if ($application->settings->is_static)
<x-forms.input placeholder="/dist" id="application.publish_directory"
label="Publish Directory" required />
@else
<x-forms.input placeholder="/" id="application.publish_directory"
label="Publish Directory" />
@endif
@endif
</div>
@if ($this->application->is_github_based() && !$this->application->is_public_repository())
<div class="pb-4">
<x-forms.textarea helper="Gitignore-style rules to filter Git based webhook deployments."
placeholder="src/pages/**" id="application.watch_paths" label="Watch Paths" />
</div>
@endif
<div class="py-4 border-b dark:border-coolgray-200">
<h3>Build</h3>
@if ($application->build_pack === 'dockerimage')
<x-forms.input
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
id="application.custom_docker_run_options" label="Custom Docker Options" />
@else
@if ($application->could_set_build_commands())
@if ($application->build_pack === 'nixpacks')
<div class="flex flex-col gap-2 xl:flex-row">
<x-forms.input helper="If you modify this, you probably need to have a nixpacks.toml"
id="application.install_command" label="Install Command" />
<x-forms.input helper="If you modify this, you probably need to have a nixpacks.toml"
id="application.build_command" label="Build Command" />
<x-forms.input helper="If you modify this, you probably need to have a nixpacks.toml"
id="application.start_command" label="Start Command" />
</div>
<div class="pt-1 text-xs">Nixpacks will detect the required configuration
automatically.
<a class="underline"
href="https://coolify.io/docs/resources/applications/index">Framework
Specific Docs</a>
</div>
@endif
@endif
<div x-data="{
activeAccordion: '',
setActiveAccordion(id) {
this.activeAccordion = (this.activeAccordion == id) ? '' : id
}
}"
class="relative w-full mx-auto mt-4 overflow-hidden text-sm font-normal">
<div x-data="{ id: $id('accordion') }" class="cursor-pointer">
<button @click="setActiveAccordion(id)"
class="flex items-center justify-between w-full p-1 text-left select-none hover:dark:text-white hover:bg-white/5"
type="button">
<h4>Advanced</h4>
<svg class="w-4 h-4 duration-200 ease-out"
:class="{ 'rotate-180': activeAccordion == id }" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div x-show="activeAccordion==id" x-collapse x-cloak class="px-2">
<div class="flex flex-col gap-2 pt-6 pb-10">
@if ($application->build_pack === 'dockercompose')
<div class="flex flex-col gap-2" x-init="$wire.dispatch('loadCompose', true)">
<div class="flex gap-2">
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/"
id="application.base_directory" label="Base Directory"
helper="Directory to use as root. Useful for monorepos." />
<x-forms.input x-bind:disabled="initLoadingCompose"
placeholder="/docker-compose.yaml"
id="application.docker_compose_location"
label="Docker Compose Location"
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
</div>
<div class="w-96">
<x-forms.checkbox instantSave
id="application.settings.is_preserve_repository_enabled"
label="Preserve Repository During Deployment"
helper="Git repository (based on the base directory settings) will be copied to the deployment directory." />
</div>
<div class="pt-4">The following commands are for advanced use cases. Only
modify them if you
know what are
you doing.</div>
<div class="flex gap-2">
<x-forms.input placeholder="docker compose build"
x-bind:disabled="initLoadingCompose"
id="application.docker_compose_custom_build_command"
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} build</span>"
label="Custom Build Command" />
<x-forms.input placeholder="docker compose up -d"
x-bind:disabled="initLoadingCompose"
id="application.docker_compose_custom_start_command"
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} up -d</span>"
label="Custom Start Command" />
</div>
</div>
@else
<div class="flex flex-col gap-2 xl:flex-row">
<x-forms.input placeholder="/" id="application.base_directory"
label="Base Directory"
helper="Directory to use as root. Useful for monorepos." />
@if ($application->build_pack === 'dockerfile' && !$application->dockerfile)
<x-forms.input placeholder="/Dockerfile"
id="application.dockerfile_location" label="Dockerfile Location"
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}</span>" />
@endif
@if ($application->build_pack === 'dockerfile')
<x-forms.input id="application.dockerfile_target_build"
label="Docker Build Stage Target"
helper="Useful if you have multi-staged dockerfile." />
@endif
@if ($application->could_set_build_commands())
@if ($application->settings->is_static)
<x-forms.input placeholder="/dist"
id="application.publish_directory" label="Publish Directory"
required />
@else
<x-forms.input placeholder="/" id="application.publish_directory"
label="Publish Directory" />
@endif
@endif
</div>
@if ($this->application->is_github_based() && !$this->application->is_public_repository())
<div class="pb-4">
<x-forms.textarea
helper="Gitignore-style rules to filter Git based webhook deployments."
placeholder="src/pages/**" id="application.watch_paths"
label="Watch Paths" />
</div>
@endif
<x-forms.input
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
id="application.custom_docker_run_options"
label="Custom Docker Options" />
@if ($application->build_pack !== 'dockercompose')
<div class="pt-2 w-96">
<x-forms.checkbox
helper="Use a build server to build your application. You can configure your build server in the Server settings. This is experimental. For more info, check the <a href='https://coolify.io/docs/knowledge-base/server/build-server' class='underline' target='_blank'>documentation</a>."
instantSave id="application.settings.is_build_server_enabled"
label="Use a Build Server? (experimental)" />
</div>
@endif
@if ($application->could_set_build_commands())
<div class="w-96">
<x-forms.checkbox instantSave id="application.settings.is_static"
label="Is it a static site?"
helper="If your application is a static site or the final build assets should be served as a static site, enable this." />
</div>
@endif
@endif
</div>
</div>
</div>
</div>
@endif
@else
<x-forms.input
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
id="application.custom_docker_run_options" label="Custom Docker Options" />
@endif
</div>
@if ($application->build_pack === 'dockercompose')
<x-forms.button wire:target='initLoadingCompose'
x-on:click="$wire.dispatch('loadCompose', false)">Reload Compose File</x-forms.button>
@@ -235,10 +283,13 @@
label="Docker Compose Content" helper="You need to modify the docker compose file."
monacoEditorLanguage="yaml" useMonacoEditor />
@endif
<div class="w-72">
<div class="w-96">
<x-forms.checkbox label="Escape special characters in labels?"
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
id="application.settings.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
<x-forms.checkbox label="Readonly labels"
helper="If you know what are you doing, you can enable this to edit the labels directly. Coolify won't update labels automatically. <br><br>Be careful, it could break the proxy configuration after you restart the container."
id="application.settings.is_container_label_readonly_enabled" instantSave></x-forms.checkbox>
</div>
@endif
@@ -264,10 +315,13 @@
<x-forms.textarea label="Container Labels" rows="15" id="customLabels"
monacoEditorLanguage="ini" useMonacoEditor></x-forms.textarea>
<div class="w-72">
<div class="w-96">
<x-forms.checkbox label="Escape special characters in labels?"
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
id="application.settings.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
<x-forms.checkbox label="Readonly labels"
helper="If you know what are you doing, you can enable this to edit the labels directly. Coolify won't update labels automatically. <br><br>Be careful, it could break the proxy configuration after you restart the container."
id="application.settings.is_container_label_readonly_enabled" instantSave></x-forms.checkbox>
</div>
<x-modal-confirmation buttonFullWidth action="resetDefaultLabels"
buttonTitle="Reset to Coolify Generated Labels">

View File

@@ -99,7 +99,7 @@
<livewire:project.shared.metrics :resource="$database" />
</div>
<div x-cloak x-show="activeTab === 'tags'">
<livewire:project.shared.tags :resource="$database" />
<livewire:project.shared.tags :resource="$database" lazy />
</div>
<div x-cloak x-show="activeTab === 'danger'">
<livewire:project.shared.danger :resource="$database" />

View File

@@ -48,8 +48,7 @@
@if ($current_step === 'repository')
<h2 class="pb-4">Select a repository</h2>
<form class="flex flex-col gap-2 pt-2" wire:submit='submit'>
<x-forms.input id="repository_url" required label="Repository Url (https:// or git@)"
helper="{!! __('repository.url') !!}" />
<x-forms.input id="repository_url" required label="Repository Url (https:// or git@)" />
<div class="flex gap-2">
<x-forms.input id="branch" required label="Branch" />
<x-forms.select wire:model.live="build_pack" label="Build Pack" required>
@@ -62,6 +61,15 @@
<x-forms.input id="publish_directory" required label="Publish Directory" />
@endif
</div>
@if ($build_pack === 'dockercompose')
<x-forms.input placeholder="/" wire:model.blur="base_directory" label="Base Directory"
helper="Directory to use as root. Useful for monorepos." />
<x-forms.input placeholder="/docker-compose.yaml" id="docker_compose_location"
label="Docker Compose Location"
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>" />
Compose file location in your repository:<span
class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>
@endif
@if ($show_is_static)
<x-forms.input type="number" required id="port" label="Port" :readonly="$is_static || $build_pack === 'static'" />
<div class="w-52">

View File

@@ -14,7 +14,6 @@
@endif
</div>
<div class="pb-4">Deploy any public or private Git repositories through a GitHub App.</div>
@if ($github_apps->count() !== 0)
<h2 class="pt-4 pb-4">Select a Github App</h2>
<div class="flex flex-col gap-2">
@@ -92,6 +91,16 @@
helper="If there is a build process involved (like Svelte, React, Next, etc..), please specify the output directory for the build assets." />
@endif
</div>
@if ($build_pack === 'dockercompose')
<x-forms.input placeholder="/" wire:model.blur="base_directory"
label="Base Directory"
helper="Directory to use as root. Useful for monorepos." />
<x-forms.input placeholder="/docker-compose.yaml" id="docker_compose_location"
label="Docker Compose Location"
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>" />
Compose file location in your repository:<span
class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>
@endif
@if ($show_is_static)
<x-forms.input type="number" id="port" label="Port" :readonly="$is_static || $build_pack === 'static'"
helper="The port your application listens on." />

View File

@@ -1,7 +1,7 @@
<div>
<h1>Create a new Application</h1>
<div class="pb-4">Deploy any public Git repositories.</div>
<form class="flex flex-col gap-2" wire:submit='load_branch'>
<form class="flex flex-col gap-2" wire:submit='loadBranch'>
<div class="flex flex-col gap-2">
<div class="flex flex-col gap-2">
<div class="flex items-end gap-2">
@@ -11,19 +11,12 @@
Check repository
</x-forms.button>
</div>
@if (!$branch_found)
<div class="px-2 pt-4">
<div class="flex flex-col pb-4">
<div>Preselect branch (eg: main):</div>
<div class='text-helper'>https://github.com/coollabsio/coolify-examples/tree/main</div>
</div>
<div>
For example application deployments, checkout <a class="underline dark:text-white"
href="https://github.com/coollabsio/coolify-examples/" target="_blank">Coolify
Examples</a>.
</div>
@endif
@if ($branch_found)
<div>
For example application deployments, checkout <a class="underline dark:text-white"
href="https://github.com/coollabsio/coolify-examples/" target="_blank">Coolify
Examples</a>.
</div>
@if ($branchFound)
@if ($rate_limit_remaining && $rate_limit_reset)
<div class="flex gap-2 py-2">
<div>Rate Limit</div>
@@ -46,23 +39,34 @@
<option value="dockerfile">Dockerfile</option>
<option value="dockercompose">Docker Compose</option>
</x-forms.select>
@if ($is_static)
@if ($isStatic)
<x-forms.input id="publish_directory" label="Publish Directory"
helper="If there is a build process involved (like Svelte, React, Next, etc..), please specify the output directory for the build assets." />
@endif
</div>
@if ($build_pack === 'dockercompose')
<x-forms.input placeholder="/" wire:model.blur="base_directory" label="Base Directory"
helper="Directory to use as root. Useful for monorepos." />
<x-forms.input placeholder="/docker-compose.yaml" id="docker_compose_location"
label="Docker Compose Location"
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>" />
Compose file location in your repository:<span
class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>
@endif
@if ($show_is_static)
<x-forms.input type="number" id="port" label="Port" :readonly="$is_static || $build_pack === 'static'"
<x-forms.input type="number" id="port" label="Port" :readonly="$isStatic || $build_pack === 'static'"
helper="The port your application listens on." />
<div class="w-52">
<x-forms.checkbox instantSave id="is_static" label="Is it a static site?"
<x-forms.checkbox instantSave id="isStatic" label="Is it a static site?"
helper="If your application is a static site or the final build assets should be served as a static site, enable this." />
</div>
@endif
@if ($build_pack === 'dockercompose' && isDev())
<div class="dark:text-warning">If you choose Docker Compose based deployments, you cannot change it afterwards.</div>
<x-forms.checkbox instantSave label="New Compose Services (only in dev mode)" id="new_compose_services"></x-forms.checkbox>
@endif
{{-- @if ($build_pack === 'dockercompose' && isDev())
<div class="dark:text-warning">If you choose Docker Compose based deployments, you cannot
change it afterwards.</div>
<x-forms.checkbox instantSave label="New Compose Services (only in dev mode)"
id="new_compose_services"></x-forms.checkbox>
@endif --}}
</div>
<x-forms.button wire:click.prevent='submit'>
Continue

View File

@@ -1,5 +1,5 @@
<x-forms.select wire:model.live="selectedEnvironment">
<option value="edit">Create/Edit Environments</option>
<option value="edit">Create / Edit</option>
<option disabled>-----</option>
@foreach ($environments as $environment)
<option value="{{ $environment->name }}">{{ $environment->name }}

View File

@@ -3,11 +3,7 @@
prevent
name collision. <br>To see the actual volume names, check the Deployable Compose file, or go to Storage
menu.</div>
<div class="pb-2 w-72">
<x-forms.checkbox label="Escape special characters in labels?"
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
</div>
<div x-cloak x-show="raw" class="font-mono">
<x-forms.textarea allowTab useMonacoEditor monacoEditorLanguage="yaml" rows="20"
id="service.docker_compose_raw">
@@ -17,6 +13,11 @@
<x-forms.textarea rows="20" readonly id="service.docker_compose">
</x-forms.textarea>
</div>
<div class="pt-2 w-72">
<x-forms.checkbox label="Escape special characters in labels?"
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
</div>
<div class="flex justify-end w-full gap-2 pt-4">
<div class="flex items-end gap-2">
<div x-cloak x-show="raw">

View File

@@ -9,7 +9,7 @@
<x-forms.checkbox id="is_multiline" label="Is Multiline?" />
@if (!$shared)
<x-forms.checkbox id="is_literal"
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it form another value. In this case, you should set this to true."
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
label="Is Literal?" />
@endif
<x-forms.button type="submit" @click="slideOverOpen=false">

View File

@@ -55,7 +55,7 @@
@if ($env->is_shared)
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
<x-forms.checkbox instantSave id="env.is_literal"
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it form another value. In this case, you should set this to true."
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
label="Is Literal?" />
@else
@if ($isSharedVariable)
@@ -65,7 +65,7 @@
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
@if (!data_get($env, 'is_multiline'))
<x-forms.checkbox instantSave id="env.is_literal"
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it form another value. In this case, you should set this to true."
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
label="Is Literal?" />
@endif
@endif

View File

@@ -13,7 +13,7 @@
it.</div>
@else
@if (!str($resource->status)->contains('running'))
<div class="alert alert-warning">Metrics are only available when the application is running!</div>
<div class="alert alert-warning">Metrics are only available when this resource is running!</div>
@else
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
<option value="5">5 minutes (live)</option>

View File

@@ -15,15 +15,17 @@
<x-forms.input id="storage.host_path" readonly helper="Directory on the host system."
label="Source Path"
helper="Warning: Changing the source path after the initial start could cause problems. Only use it when you know what are you doing." />
<x-forms.input id="storage.mount_path" label="Destination Path"
helper="Directory inside the container." required readonly />
@else
<x-forms.input id="storage.host_path" helper="Directory on the host system." label="Source Path"
helper="Warning: Changing the source path after the initial start could cause problems. Only use it when you know what are you doing." />
<x-forms.input id="storage.mount_path" label="Destination Path"
helper="Directory inside the container." required readonly />
<x-forms.button type="submit">
Update
</x-forms.button>
@endif
<x-forms.input id="storage.mount_path" label="Destination Path" helper="Directory inside the container."
required readonly />
<x-forms.button type="submit">
Update
</x-forms.button>
@else
<x-forms.input id="storage.name" required readonly />
<x-forms.input id="storage.host_path" readonly />

View File

@@ -1,10 +1,18 @@
<div>
<h2>Tags</h2>
<div class="flex flex-wrap gap-2 pt-4">
@if (data_get($this->resource, 'tags'))
@forelse (data_get($this->resource,'tags') as $tagId => $tag)
<div
class="px-2 py-1 text-center rounded select-none dark:text-white w-fit bg-neutral-200 hover:bg-neutral-300 dark:bg-coolgray-100 dark:hover:bg-coolgray-200">
<form wire:submit='submit' class="flex items-end gap-2">
<div class="w-64">
<x-forms.input label="Create new or assign existing tags"
helper="You add more at once with space separated list: web api something<br><br>If the tag does not exists, it will be created."
wire:model="newTags" placeholder="example: prod app1 user" />
</div>
<x-forms.button type="submit">Add</x-forms.button>
</form>
@if (data_get($this->resource, 'tags') && count(data_get($this->resource, 'tags')) > 0)
<h3 class="pt-4">Assigned Tags</h3>
<div class="flex flex-wrap gap-2 pt-4">
@foreach (data_get($this->resource, 'tags') as $tagId => $tag)
<div class="button">
{{ $tag->name }}
<svg wire:click="deleteTag('{{ $tag->id }}')" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24"
@@ -13,24 +21,14 @@
</path>
</svg>
</div>
@empty
<div class="py-1">No tags yet</div>
@endforelse
@endif
</div>
<form wire:submit='submit' class="flex items-end gap-2 pt-4">
<div class="w-64">
<x-forms.input label="Create new or assign existing tags"
helper="You add more at once with space separated list: web api something<br><br>If the tag does not exists, it will be created."
wire:model="new_tag" />
@endforeach
</div>
<x-forms.button type="submit">Add</x-forms.button>
</form>
@if (count($tags) > 0)
@endif
@if (count($filteredTags) > 0)
<h3 class="pt-4">Exisiting Tags</h3>
<div>Click to add quickly</div>
<div class="flex flex-wrap gap-2 pt-4">
@foreach ($tags as $tag)
@foreach ($filteredTags as $tag)
<x-forms.button wire:click="addTag('{{ $tag->id }}','{{ $tag->name }}')">
{{ $tag->name }}</x-forms.button>
@endforeach

View File

@@ -13,13 +13,13 @@
<div>
<h3>Manual Git Webhooks</h3>
@if ($githubManualWebhook && $gitlabManualWebhook)
<form wire:submit='saveSecret' class="flex flex-col gap-2">
<form wire:submit='submit' class="flex flex-col gap-2">
<div class="flex items-end gap-2">
<x-forms.input helper="Content Type in GitHub configuration could be json or form-urlencoded."
readonly label="GitHub" id="githubManualWebhook"></x-forms.input>
<x-forms.input type="password"
helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitHub."
label="GitHub Webhook Secret" id="resource.manual_webhook_secret_github"></x-forms.input>
label="GitHub Webhook Secret" id="githubManualWebhookSecret"></x-forms.input>
</div>
<a target="_blank" class="flex hover:no-underline" href="{{ $resource?->gitWebhook }}">
@@ -31,21 +31,19 @@
<x-forms.input readonly label="GitLab" id="gitlabManualWebhook"></x-forms.input>
<x-forms.input type="password"
helper="Need to set a secret to be able to use this webhook. It should match with the secret in GitLab."
label="GitLab Webhook Secret" id="resource.manual_webhook_secret_gitlab"></x-forms.input>
label="GitLab Webhook Secret" id="gitlabManualWebhookSecret"></x-forms.input>
</div>
<div class="flex gap-2">
<x-forms.input readonly label="Bitbucket" id="bitbucketManualWebhook"></x-forms.input>
<x-forms.input type="password"
helper="Need to set a secret to be able to use this webhook. It should match with the secret in Bitbucket."
label="Bitbucket Webhook Secret"
id="resource.manual_webhook_secret_bitbucket"></x-forms.input>
label="Bitbucket Webhook Secret" id="bitbucketManualWebhookSecret"></x-forms.input>
</div>
<div class="flex gap-2">
<x-forms.input readonly label="Gitea" id="giteaManualWebhook"></x-forms.input>
<x-forms.input type="password"
helper="Need to set a secret to be able to use this webhook. It should match with the secret in Gitea."
label="Gitea Webhook Secret"
id="resource.manual_webhook_secret_gitea"></x-forms.input>
label="Gitea Webhook Secret" id="giteaManualWebhookSecret"></x-forms.input>
</div>
<x-forms.button type="submit">Save</x-forms.button>
</form>

View File

@@ -136,25 +136,42 @@
@if ($server->isFunctional())
<h3 class="pt-4">Settings</h3>
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
<x-forms.input id="cleanup_after_percentage" label="Disk cleanup threshold (%)" required
helper="The disk cleanup task will run when the disk usage exceeds this threshold." />
<x-forms.input id="server.settings.concurrent_builds" label="Number of concurrent builds" required
helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
<x-forms.input id="server.settings.dynamic_timeout" label="Deployment timeout (seconds)" required
helper="You can define the maximum duration for a deployment to run before timing it out." />
<div class="flex flex-col gap-2">
<div class="flex flex-col flex-wrap gap-2 sm:flex-nowrap">
@if ($server->settings->is_force_cleanup_enabled)
<div class="w-64">
<x-forms.checkbox
helper="This will cleanup build caches / unused images / etc every 10 minutes."
instantSave id="server.settings.is_force_cleanup_enabled"
label="Force Cleanup Docker Engine" />
</div>
@else
<x-forms.input id="cleanup_after_percentage" label="Disk cleanup threshold (%)" required
helper="The disk cleanup task will run when the disk usage exceeds this threshold." />
<div class="w-64">
<x-forms.checkbox
helper="This will cleanup build caches / unused images / etc every 10 minutes."
instantSave id="server.settings.is_force_cleanup_enabled"
label="Force Cleanup Docker Engine" />
</div>
@endif
</div>
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
<x-forms.input id="server.settings.concurrent_builds" label="Number of concurrent builds" required
helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
<x-forms.input id="server.settings.dynamic_timeout" label="Deployment timeout (seconds)" required
helper="You can define the maximum duration for a deployment to run before timing it out." />
</div>
</div>
<div class="flex items-center gap-2 pt-4 pb-2">
<h3>Sentinel</h3>
@if ($server->isSentinelEnabled())
<x-forms.button wire:click='restartSentinel'>Restart</x-forms.button>
@endif
{{-- @if ($server->isSentinelEnabled()) --}}
{{-- <x-forms.button wire:click='restartSentinel'>Restart</x-forms.button> --}}
{{-- @endif --}}
</div>
<div class="w-64">
<div>Metrics are disabled until a few bugs are fixed.</div>
{{-- <div class="w-64">
<x-forms.checkbox instantSave id="server.settings.is_metrics_enabled" label="Enable Metrics" />
{{-- <x-forms.checkbox instantSave id="server.settings.is_server_api_enabled" label="Enable Server API"
helper="You need to open port 12172 on your firewall. This API will be used to gather data from your server, which makes Coolify a lot faster than relying on SSH connections." />
<x-forms.button wire:click='checkPortForServerApi'>Check Port for Server API</x-forms.button> --}}
</div>
<div class="pt-4">
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
@@ -166,7 +183,7 @@
<x-forms.input id="server.settings.metrics_history_days" label="Metrics history (days)" required
helper="How many days should the metrics data should be reserved." />
</div>
</div>
</div> --}}
@endif
</form>
</div>

View File

@@ -1,16 +1,45 @@
<form wire:submit='createGitHubApp' class="flex flex-col w-full gap-2">
<div class="pb-2">This is required, if you would like to get full integration (commit / pull request
deployments, etc)
with GitHub.</div>
<div class="flex gap-2">
<x-forms.input id="name" label="Name" required />
<x-forms.input helper="If empty, your GitHub user will be used." id="organization" label="Organization" />
<x-forms.input helper="If empty, your GitHub user will be used."
placeholder="If empty, your GitHub user will be used." id="organization" label="Organization (on GitHub)" />
</div>
<div class="flex gap-2">
<x-forms.input id="html_url" label="HTML Url" required />
<x-forms.input id="api_url" label="API Url" required />
</div>
<div class="flex gap-2">
<x-forms.input id="custom_user" label="Custom Git User" required />
<x-forms.input id="custom_port" type="number" label="Custom Git Port" required />
<div x-data="{
activeAccordion: '',
setActiveAccordion(id) {
this.activeAccordion = (this.activeAccordion == id) ? '' : id
}
}" class="relative w-full py-2 mx-auto overflow-hidden text-sm font-normal rounded-md">
<div x-data="{ id: $id('accordion') }" class="cursor-pointer">
<button @click="setActiveAccordion(id)"
class="flex items-center justify-between w-full px-1 py-2 text-left select-none hover:dark:text-white hover:bg-white/5"
type="button">
<h4>Advanced</h4>
<svg class="w-4 h-4 duration-200 ease-out" :class="{ 'rotate-180': activeAccordion == id }"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div x-show="activeAccordion==id" x-collapse x-cloak class="px-2">
<div class="py-2">Self-hosted / Enterprise GitHub details.</div>
<div class="flex flex-col gap-2 pt-0 opacity-70">
<div class="flex gap-2">
<x-forms.input id="html_url" label="HTML Url" required />
<x-forms.input id="api_url" label="API Url" required />
</div>
<div class="flex gap-2">
<x-forms.input id="custom_user" label="Custom Git User" required />
<x-forms.input id="custom_port" type="number" label="Custom Git Port" required />
</div>
</div>
</div>
</div>
</div>
@if (!isCloud())
<x-forms.checkbox id="is_system_wide" label="System Wide" />
@endif

View File

@@ -233,6 +233,7 @@ Route::middleware(['auth'])->group(function () {
Route::post('/upload/backup/{databaseUuid}', [UploadController::class, 'upload'])->name('upload.backup');
Route::get('/download/backup/{executionId}', function () {
try {
ray()->clearAll();
$team = auth()->user()->currentTeam();
if (is_null($team)) {
return response()->json(['message' => 'Team not found.'], 404);
@@ -264,14 +265,18 @@ Route::middleware(['auth'])->group(function () {
'port' => $server->port,
'username' => $server->user,
'privateKey' => $privateKeyLocation,
'root' => '/',
]);
if (! $disk->exists($filename)) {
return response()->json(['message' => 'Backup not found.'], 404);
}
return new StreamedResponse(function () use ($disk, $filename) {
if (ob_get_level()) {
ob_end_clean();
}
$stream = $disk->readStream($filename);
if ($stream === false) {
if ($stream === false || is_null($stream)) {
abort(500, 'Failed to open stream for the requested file.');
}
while (! feof($stream)) {

View File

@@ -0,0 +1,58 @@
# documentation: https://docmost.com/docs/
# slogan: Open-source collaborative wiki and documentation software
# tags: documentation, opensource, wiki, confluence, knowledge-base, notion, realtime-collaboration, notion-alternative
# logo: svgs/docmost.png
# port: 3000
services:
docmost:
image: "docmost/docmost:latest"
depends_on:
postgresql:
condition: service_healthy
redis:
condition: service_healthy
environment:
- SERVICE_FQDN_DOCMOST_3000
- APP_SECRET=$SERVICE_BASE64_APPKEY
- APP_URL=$SERVICE_FQDN_DOCMOST_3000
- DATABASE_URL=postgresql://$SERVICE_USER_POSTGRES:$SERVICE_PASSWORD_POSTGRES@postgresql/docmost?schema=public
- REDIS_URL=redis://redis:6379
volumes:
- "docmost:/app/data/storage"
healthcheck:
test:
- CMD
- curl
- "-f"
- "http://127.0.0.1:3000"
interval: 2s
timeout: 10s
retries: 20
postgresql:
image: "postgres:16-alpine"
environment:
- POSTGRES_USER=$SERVICE_USER_POSTGRES
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- POSTGRES_DB=docmost
volumes:
- "postgresql-data:/var/lib/postgresql/data"
healthcheck:
test:
- CMD-SHELL
- "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"
interval: 5s
timeout: 10s
retries: 20
redis:
image: "redis:7.2-alpine"
volumes:
- "redis-data:/data"
healthcheck:
test:
- CMD
- redis-cli
- PING
interval: 5s
timeout: 10s
retries: 20

View File

@@ -0,0 +1,53 @@
# documentation: https://www.drupal.org/about
# slogan: Drupal is a free and open-source web content management system written in PHP and distributed under the GNU General Public License.
# tags: cms, blog, content, management, postgresql
# logo: svgs/drupal.svg
services:
drupal:
image: "drupal:10-apache"
environment:
- SERVICE_FQDN_DRUPAL
- DB_HOST=postgres
- DB_NAME=postgres
- DB_USER=postgres
- DB_PASSWORD=$SERVICE_PASSWORD_POSTGRES
volumes:
- type: volume
source: drupal_modules
target: /var/www/html/modules
is_directory: true
- type: volume
source: drupal_profiles
target: /var/www/html/profiles
is_directory: true
- type: volume
source: drupal_themes
target: /var/www/html/themes
is_directory: true
- type: volume
source: drupal_sites
target: /var/www/html/sites
is_directory: true
depends_on:
- postgres
healthcheck:
test:
- CMD-SHELL
- "curl -f http://localhost:80 || exit 1"
interval: 30s
timeout: 10s
retries: 5
postgres:
image: "postgres:16"
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
healthcheck:
test:
- CMD-SHELL
- "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"
interval: 5s
timeout: 10s
retries: 20

View File

@@ -15,7 +15,7 @@ services:
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"]
test: ["CMD", "mc", "ready", "local"]
interval: 5s
timeout: 20s
retries: 10

View File

@@ -0,0 +1,190 @@
# documentation: https://docs.plane.so/self-hosting/methods/docker-compose
# slogan: The open source project management tool
# tags: plane,project-management,tool,open,source,api,nextjs,redis,postgresql,django,pm
# logo: svgs/plane.svg
x-app-env: &app-env
environment:
- WEB_URL=http://localhost
- DEBUG=${DEBUG:-0}
- CORS_ALLOWED_ORIGINS=http://localhost
# Gunicorn Workers
- GUNICORN_WORKERS=${GUNICORN_WORKERS:-1}
#DB SETTINGS
- PGHOST=plane-db
- PGDATABASE=plane
- POSTGRES_USER=$SERVICE_USER_POSTGRES
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- POSTGRES_DB=plane
- POSTGRES_PORT=5432
- PGDATA=/var/lib/postgresql/data
- DATABASE_URL=postgresql://$SERVICE_USER_POSTGRES:$SERVICE_PASSWORD_POSTGRES@plane-db/plane
# REDIS SETTINGS
- REDIS_HOST=plane-redis
- REDIS_PORT=6379
- REDIS_URL=${REDIS_URL:-redis://plane-redis:6379/}
# Application secret
- SECRET_KEY=$SERVICE_PASSWORD_64_SECRETKEY
# DATA STORE SETTINGS
- USE_MINIO=${USE_MINIO:-1}
- AWS_REGION=${AWS_REGION}
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
- AWS_S3_ENDPOINT_URL=${AWS_S3_ENDPOINT_URL:-http://plane-minio:9000}
- AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME:-uploads}
- MINIO_ROOT_USER=$SERVICE_USER_MINIO
- MINIO_ROOT_PASSWORD=$SERVICE_PASSWORD_MINIO
- BUCKET_NAME=${BUCKET_NAME:-uploads}
- FILE_SIZE_LIMIT=${FILE_SIZE_LIMIT:-5242880}
# Admin and Space URLs
- ADMIN_BASE_URL=${ADMIN_BASE_URL}
- SPACE_BASE_URL=${SPACE_BASE_URL}
- APP_BASE_URL=${APP_BASE_URL}
services:
proxy:
environment:
- SERVICE_FQDN_PLANE
- FILE_SIZE_LIMIT=${FILE_SIZE_LIMIT:-5242880}
image: makeplane/plane-proxy:stable
depends_on:
- web
- api
- space
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
interval: 2s
timeout: 10s
retries: 15
web:
<<: *app-env
image: makeplane/plane-frontend:stable
command: node web/server.js web
depends_on:
- api
- worker
healthcheck:
test: "wget -qO- http://`hostname`:3000"
interval: 2s
timeout: 10s
retries: 15
space:
<<: *app-env
image: makeplane/plane-space:stable
command: node space/server.js space
depends_on:
- api
- worker
- web
healthcheck:
test: ["CMD", "echo", "hey whats up"]
interval: 2s
timeout: 10s
retries: 15
admin:
<<: *app-env
image: makeplane/plane-admin:stable
command: node admin/server.js admin
depends_on:
- api
- web
healthcheck:
test: ["CMD", "echo", "hey whats up"]
interval: 2s
timeout: 10s
retries: 15
api:
<<: *app-env
image: makeplane/plane-backend:stable
command: ./bin/docker-entrypoint-api.sh
volumes:
- logs_api:/code/plane/logs
depends_on:
- plane-db
- plane-redis
healthcheck:
test: ["CMD", "echo", "hey whats up"]
interval: 2s
timeout: 10s
retries: 15
worker:
<<: *app-env
image: makeplane/plane-backend:stable
command: ./bin/docker-entrypoint-worker.sh
volumes:
- logs_worker:/code/plane/logs
depends_on:
- api
- plane-db
- plane-redis
healthcheck:
test: ["CMD", "echo", "hey whats up"]
interval: 2s
timeout: 10s
retries: 15
beat-worker:
<<: *app-env
image: makeplane/plane-backend:stable
command: ./bin/docker-entrypoint-beat.sh
volumes:
- logs_beat-worker:/code/plane/logs
depends_on:
- api
- plane-db
- plane-redis
healthcheck:
test: ["CMD", "echo", "hey whats up"]
interval: 2s
timeout: 10s
retries: 15
migrator:
<<: *app-env
image: makeplane/plane-backend:stable
restart: "no"
command: ./bin/docker-entrypoint-migrator.sh
volumes:
- logs_migrator:/code/plane/logs
depends_on:
- plane-db
- plane-redis
plane-db:
<<: *app-env
image: postgres:15.5-alpine
command: postgres -c 'max_connections=1000'
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 20s
retries: 10
plane-redis:
<<: *app-env
image: valkey/valkey:7.2.5-alpine
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 20s
retries: 10
plane-minio:
<<: *app-env
image: minio/minio:latest
command: server /export --console-address ":9090"
volumes:
- uploads:/export
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 5s
timeout: 20s
retries: 10

View File

@@ -53,7 +53,7 @@ services:
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"]
test: ["CMD", "mc", "ready", "local"]
interval: 5s
timeout: 20s
retries: 10

View File

@@ -301,7 +301,7 @@ services:
- DEFAULT_ORGANIZATION_NAME=${STUDIO_DEFAULT_ORGANIZATION:-Default Organization}
- DEFAULT_PROJECT_NAME=${STUDIO_DEFAULT_PROJECT:-Default Project}
- SUPABASE_URL=http://supabase-kong:8000
- SUPABASE_URL=${SERVICE_FQDN_SUPABASEKONG:-http://supabase-kong:8000}
- SUPABASE_PUBLIC_URL=${SERVICE_FQDN_SUPABASEKONG}
- SUPABASE_ANON_KEY=${SERVICE_SUPABASEANON_KEY}
- SUPABASE_SERVICE_KEY=${SERVICE_SUPABASESERVICE_KEY}
@@ -1182,7 +1182,7 @@ services:
retries: 3
environment:
- JWT_SECRET=${SERVICE_PASSWORD_JWT}
- SUPABASE_URL=http://supabase-kong:8000
- SUPABASE_URL=${SERVICE_FQDN_SUPABASEKONG:-http://supabase-kong:8000}
- SUPABASE_ANON_KEY=${SERVICE_SUPABASEANON_KEY}
- SUPABASE_SERVICE_ROLE_KEY=${SERVICE_SUPABASESERVICE_KEY}
- SUPABASE_DB_URL=postgresql://postgres:${SERVICE_PASSWORD_POSTGRES}@${POSTGRES_HOSTNAME:-supabase-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-postgres}

View File

@@ -12,13 +12,15 @@ services:
- SERVER_URL=$SERVICE_FQDN_TWENTY
- FRONT_BASE_URL=$SERVICE_FQDN_TWENTY
- ENABLE_DB_MIGRATIONS=true
- SIGN_IN_PREFILLED=false
- CACHE_STORAGE_TYPE=${CACHE_STORAGE_TYPE:-redis}
- REDIS_HOST=redis
- REDIS_PORT=6379
- STORAGE_TYPE=${STORAGE_TYPE:-local}
- STORAGE_S3_REGION=$STORAGE_S3_REGION
- STORAGE_S3_NAME=$STORAGE_S3_NAME
- STORAGE_S3_ENDPOINT=$STORAGE_S3_ENDPOINT
# https://twenty.com/developers/section/self-hosting/self-hosting-var#security
- API_RATE_LIMITING_TTL=${API_RATE_LIMITING_TTL:-100}
- API_RATE_LIMITING_LIMIT=${API_RATE_LIMITING_LIMIT:-100}
# https://twenty.com/developers/section/self-hosting/self-hosting-var#tokens
- ACCESS_TOKEN_SECRET=$SERVICE_BASE64_32_ACCESS
- LOGIN_TOKEN_SECRET=$SERVICE_BASE64_32_LOGIN
- REFRESH_TOKEN_SECRET=$SERVICE_BASE64_32_REFRESH
@@ -26,6 +28,26 @@ services:
- POSTGRES_ADMIN_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- PG_DATABASE_URL=postgres://postgres:$SERVICE_PASSWORD_POSTGRES@postgres:5432/default
# https://twenty.com/developers/section/self-hosting/self-hosting-var#auth
- IS_SIGN_UP_DISABLED=${IS_SIGN_UP_DISABLED:-false}
- PASSWORD_RESET_TOKEN_EXPIRES_IN=${PASSWORD_RESET_TOKEN_EXPIRES_IN:-5m}
# https://twenty.com/developers/section/self-hosting/self-hosting-var#workspace-cleaning
- WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION=$WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION
- WORKSPACE_INACTIVE_DAYS_BEFORE_DELETION=$WORKSPACE_INACTIVE_DAYS_BEFORE_DELETION
# https://twenty.com/developers/section/self-hosting/self-hosting-var#captcha
- STORAGE_TYPE=${STORAGE_TYPE:-local}
- STORAGE_S3_REGION=$STORAGE_S3_REGION
- STORAGE_S3_NAME=$STORAGE_S3_NAME
- STORAGE_S3_ENDPOINT=$STORAGE_S3_ENDPOINT
- STORAGE_S3_ACCESS_KEY_ID=$STORAGE_S3_ACCESS_KEY_ID
- STORAGE_S3_SECRET_ACCESS_KEY=$STORAGE_S3_SECRET_ACCESS_KEY
# https://twenty.com/developers/section/self-hosting/self-hosting-var#message-queue
- MESSAGE_QUEUE_TYPE=$MESSAGE_QUEUE_TYPE
# https://twenty.com/developers/section/self-hosting/self-hosting-var#email
- EMAIL_FROM_ADDRESS=$EMAIL_FROM_ADDRESS
- EMAIL_FROM_NAME=$EMAIL_FROM_NAME
- EMAIL_SYSTEM_ADDRESS=$EMAIL_SYSTEM_ADDRESS
@@ -35,10 +57,12 @@ services:
- EMAIL_SMTP_USER=$EMAIL_SMTP_USER
- EMAIL_SMTP_PASSWORD=$EMAIL_SMTP_PASSWORD
# https://twenty.com/developers/section/self-hosting/self-hosting-var#debug-/-development
- SIGN_IN_PREFILLED=false
- DEBUG_MODE=${DEBUG_MODE:-false}
# https://twenty.com/developers/section/self-hosting/self-hosting-var#telemetry
- TELEMETRY_ENABLED=${TELEMETRY_ENABLED:-false}
- CACHE_STORAGE_TYPE=${CACHE_STORAGE_TYPE:-redis}
- REDIS_HOST=redis
- REDIS_PORT=6379
depends_on:
postgres:
condition: service_healthy

View File

@@ -0,0 +1,37 @@
# documentation: https://vikunja.io
# slogan: The open-source, self-hostable to-do app. Organize everything, on all platforms.
# tags: productivity,todo
# logo: svgs/vikunja.svg
# port: 3456
services:
vikunja:
image: vikunja/vikunja
environment:
- SERVICE_FQDN_VIKUNJA
- VIKUNJA_SERVICE_PUBLICURL=$SERVICE_FQDN_VIKUNJA
- VIKUNJA_SERVICE_JWTSECRET=$SERVICE_PASSWORD_JWTSECRET
- VIKUNJA_SERVICE_ENABLEREGISTRATION=true
- VIKUNJA_DATABASE_TYPE=postgres
- VIKUNJA_DATABASE_HOST=postgresql
- VIKUNJA_DATABASE_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
- VIKUNJA_DATABASE_USER=${SERVICE_USER_POSTGRESQL}
- VIKUNJA_DATABASE_DATABASE=${POSTGRESQL_DATABASE}
volumes:
- vikunja-data:/app/vikunja/
depends_on:
postgresql:
condition: service_healthy
postgresql:
image: postgres:16-alpine
volumes:
- vikunja-postgresql-data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${SERVICE_USER_POSTGRESQL}
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
- POSTGRES_DB=${POSTGRESQL_DATABASE}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 20s
retries: 10

View File

@@ -12,10 +12,16 @@ services:
- VIKUNJA_SERVICE_PUBLICURL=$SERVICE_FQDN_VIKUNJA
- VIKUNJA_SERVICE_JWTSECRET=$SERVICE_PASSWORD_JWTSECRET
- VIKUNJA_SERVICE_ENABLEREGISTRATION=true
- VIKUNJA_DATABASE_PATH=/db/vikunja.db
- VIKUNJA_DATABASE_TYPE=sqlite
volumes:
- vikunja-data:/app/vikunja/
healthcheck:
test: ["CMD", "wget", "--spider", "http://127.0.0.1:3456"]
interval: 5s
timeout: 20s
retries: 10
- vikunja-sqlite-data:/db
depends_on:
- init
init:
image: busybox
restart: no
volumes:
- vikunja-sqlite-data:/db
command: ["sh", "-c", "touch /db/vikunja.db && chown -R 1000 /db"]

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
{
"coolify": {
"v4": {
"version": "4.0.0-beta.309"
"version": "4.0.0-beta.316"
}
}
}
}