Compare commits

...

29 Commits

Author SHA1 Message Date
Andras Bacsai
d921456036 Merge pull request #1903 from coollabsio/next
v4.0.0-beta.246
2024-03-27 14:55:41 +01:00
Andras Bacsai
6cc93250b8 fix: uis 2024-03-27 14:51:58 +01:00
Andras Bacsai
39082541ff fix: duplicated generated fqdns are now working 2024-03-27 14:51:43 +01:00
Andras Bacsai
8cff40fdd4 fix: disabled inputs are not trucated 2024-03-27 14:51:20 +01:00
Andras Bacsai
a777db1234 fix: async service loads 2024-03-27 14:50:56 +01:00
Andras Bacsai
3fca169096 feat: change page width 2024-03-27 12:45:53 +01:00
Andras Bacsai
58b451f616 Update GitHub Actions versions 2024-03-27 11:59:01 +01:00
Andras Bacsai
f9b82f711f Update platform tags in development-build.yml 2024-03-27 11:48:04 +01:00
Andras Bacsai
d92b5db320 Refactor forgot password page layout 2024-03-27 11:41:14 +01:00
Andras Bacsai
a164e4bf3a Update Docker build configurations for arm64-v8 architecture 2024-03-27 11:38:21 +01:00
Andras Bacsai
be3cbd9e21 Refactor email settings message in email.blade.php 2024-03-27 11:37:09 +01:00
Andras Bacsai
43fed96af1 fix: autoupdater 2024-03-27 11:35:57 +01:00
Andras Bacsai
3d3d31ef29 fix: name/from address required for resend 2024-03-27 11:28:12 +01:00
Andras Bacsai
d51e70bcaa temporary disable containerrestarted for tcp proxies 2024-03-27 11:13:40 +01:00
Andras Bacsai
32f4c6c982 Update documentation links 2024-03-27 11:07:29 +01:00
Andras Bacsai
8f47761200 ui: fix log outputs 2024-03-27 10:54:17 +01:00
Andras Bacsai
342ebecef2 Remove unnecessary version entry for "coolify" main branch 2024-03-26 14:19:25 +01:00
Andras Bacsai
220a8fe2cc Update container and version configurations 2024-03-26 14:18:33 +01:00
Andras Bacsai
480cb00098 Merge pull request #1893 from coollabsio/next
v4.0.0-beta.245
2024-03-26 13:58:02 +01:00
Andras Bacsai
07ed550dc2 fix: PR deployments should not be distributed to 2 servers 2024-03-26 13:56:30 +01:00
Andras Bacsai
6045870398 fix: scheduled backup for other service databases (supabase) 2024-03-26 13:50:44 +01:00
Andras Bacsai
2bf102cdf1 fix: multline env in dev mode 2024-03-26 13:36:06 +01:00
Andras Bacsai
889a5b2bce ui: fix 2024-03-26 13:35:55 +01:00
Andras Bacsai
df964a094b Remove commented out code for delayed jobs in Kernel.php 2024-03-26 13:27:06 +01:00
Andras Bacsai
e395d4ecee Update version description in bug report template 2024-03-26 13:26:00 +01:00
Andras Bacsai
d077e0c83c fix: realtime connection check 2024-03-26 13:25:10 +01:00
Andras Bacsai
d931241edc fix: update resources view 2024-03-26 12:51:42 +01:00
Andras Bacsai
64a9a72457 fix: ui for tags 2024-03-26 11:52:45 +01:00
Andras Bacsai
a8417aca16 cloud: run cleanup every day 2024-03-26 11:17:48 +01:00
67 changed files with 591 additions and 492 deletions

View File

@@ -21,6 +21,6 @@ body:
- type: input
attributes:
label: Version
description: Coolify's version (see bottom left corner).
description: Coolify's version (see top of your screen).
validations:
required: true

View File

@@ -18,15 +18,15 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image and push to registry
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
no-cache: true
context: .
@@ -40,15 +40,15 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image and push to registry
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
no-cache: true
context: .
@@ -64,13 +64,13 @@ jobs:
needs: [ amd64, aarch64 ]
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to ghcr.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}

View File

@@ -18,15 +18,15 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image and push to registry
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
no-cache: true
context: .
@@ -40,15 +40,15 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image and push to registry
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
no-cache: true
context: .
@@ -64,13 +64,13 @@ jobs:
needs: [ amd64, aarch64 ]
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to ghcr.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}

View File

@@ -18,15 +18,15 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image and push to registry
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
no-cache: true
context: .
@@ -40,15 +40,15 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image and push to registry
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
no-cache: true
context: .
@@ -64,13 +64,13 @@ jobs:
needs: [ amd64, aarch64 ]
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to ghcr.io
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}

View File

@@ -14,7 +14,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Cache Docker layers
uses: actions/cache@v2
with:

View File

@@ -30,5 +30,5 @@ Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if y
Mails are caught by Mailpit: `localhost:8025`
## New Service Contribution
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
Check out the docs [here](https://coolify.io/docs/resources/services/add-service).

View File

@@ -13,7 +13,7 @@ class InstallDocker
{
$supported_os_type = $server->validateOS();
if (!$supported_os_type) {
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/servers#install-docker-engine-manually">documentation</a>.');
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
}
ray('Installing Docker on server: ' . $server->name . ' (' . $server->ip . ')' . ' with OS type: ' . $supported_os_type);
$dockerVersion = '24.0';

View File

@@ -12,10 +12,12 @@ class UpdateCoolify
public ?Server $server = null;
public ?string $latestVersion = null;
public ?string $currentVersion = null;
public bool $async = false;
public function handle(bool $force)
public function handle(bool $force = false, bool $async = false)
{
try {
$this->async = $async;
$settings = InstanceSettings::get();
ray('Running InstanceAutoUpdateJob');
$this->server = Server::find(0);
@@ -56,17 +58,31 @@ class UpdateCoolify
{
if (isDev()) {
ray("Running update on local docker container. Updating to $this->latestVersion");
remote_process([
"sleep 10"
], $this->server);
if ($this->async) {
ray('Running async update');
remote_process([
"sleep 10"
], $this->server);
} else {
instant_remote_process([
"sleep 10"
], $this->server);
}
ray('Update done');
return;
} else {
ray('Running update on production server');
remote_process([
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
], $this->server);
if ($this->async) {
remote_process([
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
], $this->server);
} else {
instant_remote_process([
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
], $this->server);
}
return;
}
}

View File

@@ -20,7 +20,7 @@ class CleanupDatabase extends Command
$keep_days = 60;
echo "Keep days: $keep_days\n";
// Cleanup failed jobs table
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(7));
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(1));
$count = $failed_jobs->count();
echo "Delete $count entries from failed_jobs.\n";
if ($this->option('yes')) {

View File

@@ -48,9 +48,7 @@ class Kernel extends ConsoleKernel
$this->pull_helper_image($schedule);
$this->check_scheduled_tasks($schedule);
if (!isCloud()) {
$schedule->command('cleanup:database --yes')->daily();
}
$schedule->command('cleanup:database --yes')->daily();
}
}
private function pull_helper_image($schedule)
@@ -72,43 +70,14 @@ class Kernel extends ConsoleKernel
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
}
foreach ($containerServers as $server) {
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
$schedule->job(new ContainerStatusJob($server))->everyTwoMinutes()->onOneServer();
if ($server->isLogDrainEnabled()) {
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
$schedule->job(new CheckLogDrainContainerJob($server))->everyTwoMinutes()->onOneServer();
}
}
foreach ($servers as $server) {
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
$schedule->job(new ServerStatusJob($server))->everyTwoMinutes()->onOneServer();
}
// Delayed Jobs
// foreach ($containerServers as $server) {
// $schedule
// ->call(function () use ($server) {
// $randomSeconds = rand(1, 40);
// $job = new ContainerStatusJob($server);
// $job->delay($randomSeconds);
// ray('dispatching container status job in ' . $randomSeconds . ' seconds');
// dispatch($job);
// })->name('container-status-' . $server->id)->everyMinute()->onOneServer();
// if ($server->isLogDrainEnabled()) {
// $schedule
// ->call(function () use ($server) {
// $randomSeconds = rand(1, 40);
// $job = new CheckLogDrainContainerJob($server);
// $job->delay($randomSeconds);
// dispatch($job);
// })->name('log-drain-container-check-' . $server->id)->everyMinute()->onOneServer();
// }
// }
// foreach ($servers as $server) {
// $schedule
// ->call(function () use ($server) {
// $randomSeconds = rand(1, 40);
// $job = new ServerStatusJob($server);
// $job->delay($randomSeconds);
// dispatch($job);
// })->name('server-status-job-' . $server->id)->everyMinute()->onOneServer();
// }
}
private function instance_auto_update($schedule)
{

View File

@@ -44,7 +44,7 @@ class Deploy extends Controller
$force = $request->query->get('force') ?? false;
if ($uuids && $tags) {
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
}
if (is_null($teamId)) {
return invalid_token();
@@ -54,7 +54,7 @@ class Deploy extends Controller
} else if ($uuids) {
return $this->by_uuids($uuids, $teamId, $force);
}
return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
}
private function by_uuids(string $uuid, int $teamId, bool $force = false)
{
@@ -62,7 +62,7 @@ class Deploy extends Controller
$uuids = collect(array_filter($uuids));
if (count($uuids) === 0) {
return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
}
$deployments = collect();
$payload = collect();
@@ -81,7 +81,7 @@ class Deploy extends Controller
$payload->put('deployments', $deployments->toArray());
return response()->json($payload->toArray(), 200);
}
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
}
public function by_tags(string $tags, int $team_id, bool $force = false)
{
@@ -89,7 +89,7 @@ class Deploy extends Controller
$tags = collect(array_filter($tags));
if (count($tags) === 0) {
return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
}
$message = collect([]);
$deployments = collect();
@@ -127,7 +127,7 @@ class Deploy extends Controller
return response()->json($payload->toArray(), 200);
}
return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
}
public function deploy_resource($resource, bool $force = false): array
{

View File

@@ -26,7 +26,7 @@ class Team extends Controller
$teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first();
if (is_null($team)) {
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id"], 404);
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid"], 404);
}
return response()->json($team);
}
@@ -40,7 +40,7 @@ class Team extends Controller
$teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first();
if (is_null($team)) {
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id-members"], 404);
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid-members"], 404);
}
return response()->json($team->members);
}

View File

@@ -737,7 +737,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
}
if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) {
$this->application_deployment_queue->addLogEntry("There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/frameworks/laravel#requirements", 'stderr');
$this->application_deployment_queue->addLogEntry("There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/resources/laravel", 'stderr');
}
}
private function rolling_update()
@@ -898,6 +898,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ($this->application->additional_networks->count() === 0) {
return;
}
if ($this->pull_request_id !== 0) {
return;
}
$destination_ids = $this->application->additional_networks->pluck('id');
if ($this->server->isSwarm()) {
$this->application_deployment_queue->addLogEntry("Additional destinations are not supported in swarm mode.");

View File

@@ -170,16 +170,13 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
})->first();
if (!$foundTcpProxy) {
StartDatabaseProxy::run($service_db);
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
}
}
}
}
} else {
$database = $databases->where('uuid', $uuid)->first();
if ($uuid == 'postgresql') {
ray($database);
}
if ($database) {
$isPublic = data_get($database, 'is_public');
$foundDatabases[] = $database->id;
@@ -187,7 +184,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
if ($statusFromDb !== $containerStatus) {
$database->update(['status' => $containerStatus]);
}
ray($database);
if ($isPublic) {
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) {
@@ -197,7 +193,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
}
})->first();
if (!$foundTcpProxy) {
ray('asdffff');
StartDatabaseProxy::run($database);
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
}

View File

@@ -95,7 +95,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
$databaseType = $this->database->databaseType();
$serviceUuid = $this->database->service->uuid;
$serviceName = str($this->database->service->name)->slug();
if ($databaseType === 'standalone-postgresql') {
if (str($databaseType)->contains('postgres')) {
$this->container_name = "{$this->database->name}-$serviceUuid";
$this->directory_name = $serviceName . '-' . $this->container_name;
$commands[] = "docker exec $this->container_name env | grep POSTGRES_";
@@ -120,7 +120,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
} else {
$databasesToBackup = $this->database->postgres_user;
}
} else if ($databaseType === 'standalone-mysql') {
} else if (str($databaseType)->contains('mysql')) {
$this->container_name = "{$this->database->name}-$serviceUuid";
$this->directory_name = $serviceName . '-' . $this->container_name;
$commands[] = "docker exec $this->container_name env | grep MYSQL_";
@@ -143,7 +143,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
} else {
throw new \Exception('MYSQL_DATABASE not found');
}
} else if ($databaseType === 'standalone-mariadb') {
} else if (str($databaseType)->contains('mariadb')) {
$this->container_name = "{$this->database->name}-$serviceUuid";
$this->directory_name = $serviceName . '-' . $this->container_name;
$commands[] = "docker exec $this->container_name env";
@@ -190,32 +190,32 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
}
if (is_null($databasesToBackup)) {
if ($databaseType === 'standalone-postgresql') {
if (str($databaseType)->contains('postgres')) {
$databasesToBackup = [$this->database->postgres_db];
} else if ($databaseType === 'standalone-mongodb') {
} else if (str($databaseType)->contains('mongodb')) {
$databasesToBackup = ['*'];
} else if ($databaseType === 'standalone-mysql') {
} else if (str($databaseType)->contains('mysql')) {
$databasesToBackup = [$this->database->mysql_database];
} else if ($databaseType === 'standalone-mariadb') {
} else if (str($databaseType)->contains('mariadb')) {
$databasesToBackup = [$this->database->mariadb_database];
} else {
return;
}
} else {
if ($databaseType === 'standalone-postgresql') {
if (str($databaseType)->contains('postgres')) {
// Format: db1,db2,db3
$databasesToBackup = explode(',', $databasesToBackup);
$databasesToBackup = array_map('trim', $databasesToBackup);
} else if ($databaseType === 'standalone-mongodb') {
} else if (str($databaseType)->contains('mongodb')) {
// Format: db1:collection1,collection2|db2:collection3,collection4
$databasesToBackup = explode('|', $databasesToBackup);
$databasesToBackup = array_map('trim', $databasesToBackup);
ray($databasesToBackup);
} else if ($databaseType === 'standalone-mysql') {
} else if (str($databaseType)->contains('mysql')) {
// Format: db1,db2,db3
$databasesToBackup = explode(',', $databasesToBackup);
$databasesToBackup = array_map('trim', $databasesToBackup);
} else if ($databaseType === 'standalone-mariadb') {
} else if (str($databaseType)->contains('mariadb')) {
// Format: db1,db2,db3
$databasesToBackup = explode(',', $databasesToBackup);
$databasesToBackup = array_map('trim', $databasesToBackup);
@@ -235,7 +235,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
$size = 0;
ray('Backing up ' . $database);
try {
if ($databaseType === 'standalone-postgresql') {
if (str($databaseType)->contains('postgres')) {
$this->backup_file = "/pg-dump-$database-" . Carbon::now()->timestamp . ".dmp";
$this->backup_location = $this->backup_dir . $this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([
@@ -244,7 +244,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
'scheduled_database_backup_id' => $this->backup->id,
]);
$this->backup_standalone_postgresql($database);
} else if ($databaseType === 'standalone-mongodb') {
} else if (str($databaseType)->contains('mongodb')) {
if ($database === '*') {
$database = 'all';
$databaseName = 'all';
@@ -263,7 +263,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
'scheduled_database_backup_id' => $this->backup->id,
]);
$this->backup_standalone_mongodb($database);
} else if ($databaseType === 'standalone-mysql') {
} else if (str($databaseType)->contains('mysql')) {
$this->backup_file = "/mysql-dump-$database-" . Carbon::now()->timestamp . ".dmp";
$this->backup_location = $this->backup_dir . $this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([
@@ -272,7 +272,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
'scheduled_database_backup_id' => $this->backup->id,
]);
$this->backup_standalone_mysql($database);
} else if ($databaseType === 'standalone-mariadb') {
} else if (str($databaseType)->contains('mariadb')) {
$this->backup_file = "/mariadb-dump-$database-" . Carbon::now()->timestamp . ".dmp";
$this->backup_location = $this->backup_dir . $this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([

View File

@@ -23,6 +23,6 @@ class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncr
public function handle(): void
{
UpdateCoolify::run($this->force);
UpdateCoolify::run(force: $this->force, async: false);
}
}

View File

@@ -251,7 +251,7 @@ class General extends Component
if ($this->application->additional_servers->count() === 0) {
foreach ($domains as $domain) {
if (!validate_dns_entry($domain, $this->application->destination->server)) {
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='dark:text-white underline' href='https://coolify.io/docs/dns-settings'>documentation</a> for further help.");
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
}
}
}

View File

@@ -56,11 +56,11 @@ class Heading extends Component
return;
}
if (data_get($this->application, 'settings.is_build_server_enabled') && str($this->application->docker_registry_image_name)->isEmpty()) {
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/build-server">documentation</a>');
return;
}
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
return;
}
$this->setDeploymentUuid();
@@ -99,7 +99,7 @@ class Heading extends Component
public function restart()
{
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
return;
}
$this->setDeploymentUuid();

View File

@@ -49,7 +49,6 @@ class Select extends Component
}
public function render()
{
$this->loadServices();
return view('livewire.project.new.select');
}
@@ -74,6 +73,7 @@ class Select extends Component
public function loadServices(bool $force = false)
{
try {
$this->loadingServices = true;
if (count($this->allServices) > 0 && !$force) {
if (!$this->search) {
$this->services = $this->allServices;

View File

@@ -56,7 +56,7 @@ class Destination extends Component
public function redeploy(int $network_id, int $server_id)
{
if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) {
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
return;
}
$deployment_uuid = new Cuid2(7);

View File

@@ -98,6 +98,7 @@ class All extends Component
}
}
$environment->is_build_time = false;
$environment->is_multiline = false;
$environment->is_preview = $isPreview ? true : false;
switch ($this->resource->type()) {
case 'application':

View File

@@ -1,25 +0,0 @@
<?php
namespace App\Livewire;
use Livewire\Component;
class RealtimeConnection extends Component
{
public $checkConnection = false;
public $showNotification = false;
public $isNotificationEnabled = true;
public function render()
{
return view('livewire.realtime-connection');
}
public function disable()
{
auth()->user()->update(['is_notification_realtime_enabled' => false]);
$this->showNotification = false;
}
public function mount() {
$this->isNotificationEnabled = auth()->user()->is_notification_realtime_enabled;
$this->checkConnection = auth()->user()->id === 0;
}
}

View File

@@ -82,7 +82,7 @@ class Form extends Component
$this->server->settings->is_usable = true;
$this->server->settings->save();
} else {
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh">documentation</a> for further help.');
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.');
return;
}
}

View File

@@ -39,7 +39,7 @@ class ShowPrivateKey extends Component
if ($uptime) {
$this->dispatch('success', 'Server is reachable.');
} else {
$this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh#openssh">documentation</a> for further help.');
$this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.');
return;
}
} catch (\Throwable $e) {

View File

@@ -75,7 +75,7 @@ class ValidateAndInstall extends Component
{
$this->uptime = $this->server->validateConnection();
if (!$this->uptime) {
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh">documentation</a> for further help.';
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.';
return;
}
$this->dispatch('validateOS');

View File

@@ -58,6 +58,8 @@ class Email extends Component
try {
$this->resetErrorBag();
$this->validate([
'settings.smtp_from_address' => 'required|email',
'settings.smtp_from_name' => 'required',
'settings.resend_api_key' => 'required'
]);
$this->settings->save();
@@ -90,6 +92,8 @@ class Email extends Component
try {
$this->resetErrorBag();
$this->validate([
'settings.smtp_from_address' => 'required|email',
'settings.smtp_from_name' => 'required',
'settings.smtp_host' => 'required',
'settings.smtp_port' => 'required|numeric',
'settings.smtp_encryption' => 'nullable',

View File

@@ -37,8 +37,8 @@ class Upgrade extends Component
return;
}
$this->showProgress = true;
UpdateCoolify::run(true);
$this->dispatch('success', "Upgrading to {$this->latestVersion} version...");
UpdateCoolify::run(force: true, async: true);
$this->dispatch('success', "Updating Coolify to {$this->latestVersion} version...");
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -53,13 +53,13 @@ class HighDiskUsage extends Notification implements ShouldQueue
public function toDiscord(): string
{
$message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/automated-cleanup.";
$message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.";
return $message;
}
public function toTelegram(): array
{
return [
"message" => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/automated-cleanup."
"message" => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup."
];
}
}

View File

@@ -7,5 +7,5 @@ function get_team_id_from_token()
}
function invalid_token()
{
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api-reference/authorization'], 400);
}

View File

@@ -110,7 +110,7 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n
ray($error);
if ($error instanceof TooManyRequestsException) {
if (isset($livewire)) {
return $livewire->dispatch('error', 'Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.');
return $livewire->dispatch('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.");
}
return "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.";
}
@@ -944,11 +944,10 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if (!$isDatabase) {
if ($savedService->fqdn) {
$fqdn = $savedService->fqdn . ',' . $fqdn;
data_set($savedService, 'fqdn', $savedService->fqdn . ',' . $fqdn);
} else {
$fqdn = $fqdn;
data_set($savedService, 'fqdn', $fqdn);
}
$savedService->fqdn = $fqdn;
$savedService->save();
}
EnvironmentVariable::create([
@@ -960,7 +959,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
]);
}
// Caddy needs exact port in some cases.
if ($predefinedPort && !$key->endsWith("_{$predefinedPort}")) {
if ($resource->server->proxyType() === 'CADDY') {
$env = EnvironmentVariable::where([
@@ -1459,13 +1457,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
])->first();
$value = Str::of(replaceVariables($value));
$key = $value;
if ($value->startsWith('SERVICE_')) {
$foundEnv = EnvironmentVariable::where([
'key' => $key,
'application_id' => $resource->id,
])->first();
['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value);
ray($command, $generatedValue);
if (!is_null($command)) {
if ($command?->value() === 'FQDN' || $command?->value() === 'URL') {
if (Str::lower($forService) === $serviceName) {

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.244',
'release' => '4.0.0-beta.246',
// 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.244';
return '4.0.0-beta.246';

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('users', function (Blueprint $table) {
$table->dropColumn('is_notification_realtime_enabled');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('is_notification_realtime_enabled')->default(true);
});
}
};

View File

@@ -1,3 +0,0 @@
<svg width="45.34" height="64" viewBox="0 0 17 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#FCD34D" d="M11.403 18.751v4.499c-.01.41-.34.74-.748.75H6.159a.768.768 0 0 1-.749-.748v-4.5c.01-.41.34-.739.749-.749h4.5c.41.01.74.34.75.749v.001zm5.923-11.247a6.306 6.306 0 0 1-.962 3.354l.015-.026a5.462 5.462 0 0 1-1.021 1.108l-.01.008c-.321.282-.672.55-1.042.794l-.036.022q-.413.253-1.144.665a3.71 3.71 0 0 0-1.275 1.204l-.009.014a2.535 2.535 0 0 0-.515 1.243l-.001.012a.978.978 0 0 1-.226.611l.001-.002a.652.652 0 0 1-.524.29h-4.5a.563.563 0 0 1-.479-.343l-.001-.004a1.394 1.394 0 0 1-.197-.702v-.845a4.356 4.356 0 0 1 1.219-2.935l-.001.001A7.945 7.945 0 0 1 9.251 9.96l.048-.02a4.627 4.627 0 0 0 1.574-1.049l.001-.001a2.094 2.094 0 0 0 .469-1.429v.005a1.695 1.695 0 0 0-.863-1.382l-.009-.004a3.436 3.436 0 0 0-2.018-.599h.003a3.53 3.53 0 0 0-2.039.552l.014-.009A12.825 12.825 0 0 0 4.45 8.149l-.025.035a.73.73 0 0 1-.581.3a.897.897 0 0 1-.472-.152l.003.002L.301 5.991a.732.732 0 0 1-.29-.464L.01 5.523a.747.747 0 0 1 .105-.527l-.002.003C1.77 2 4.912.003 8.522.003c.103 0 .205.002.307.005h-.015a8.362 8.362 0 0 1 3.074.602l-.057-.02a10.2 10.2 0 0 1 2.757 1.571l-.02-.016a7.838 7.838 0 0 1 1.966 2.349l.02.041c.483.857.768 1.881.769 2.971z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -116,7 +116,12 @@ tr td:first-child {
.alert-error {
@apply flex items-center gap-2 text-error;
}
.tag {
@apply px-2 py-1 cursor-pointer box-description dark:bg-coolgray-100 dark:hover:bg-coolgray-300 bg-neutral-100 hover:bg-neutral-200
}
.add-tag {
@apply flex items-center px-2 text-xs cursor-pointer dark:text-neutral-500/20 text-neutral-500 group-hover:text-neutral-700 group-hover:dark:text-white dark:hover:bg-coolgray-300 hover:bg-neutral-200;
}
.dropdown-item {
@apply relative flex cursor-pointer select-none dark:text-white hover:bg-neutral-100 dark:hover:bg-coollabs items-center pr-4 pl-2 py-1 text-xs justify-start outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 gap-2 w-full;
}
@@ -193,16 +198,16 @@ tr td:first-child {
@apply flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 bg-white border border-neutral-200 dark:border-black hover:bg-neutral-100 dark:hover:bg-coollabs-100 dark:hover:text-white hover:no-underline;
}
.box-boarding {
@apply flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 dark:text-white bg-neutral-50 border border-neutral-200 dark:border-black hover:bg-neutral-100 dark:hover:bg-coollabs-100 dark:hover:text-white hover:text-black hover:no-underline text-black;
@apply flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 dark:text-white bg-neutral-50 border border-neutral-200 dark:border-black hover:bg-neutral-100 dark:hover:bg-coollabs-100 dark:hover:text-white hover:text-black hover:no-underline text-black ;
}
.box-without-bg {
@apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem] border border-neutral-200 dark:border-black;
}
.on-box {
@apply rounded hover:bg-neutral-300 dark:hover:bg-coolgray-500/20;
}
.box-without-bg {
@apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem];
}
.box-title {
@apply font-bold text-black dark:text-white group-hover:dark:text-white;
@@ -275,7 +280,7 @@ tr td:first-child {
}
.fullscreen {
@apply fixed top-0 left-0 w-full h-full z-[9999] bg-coolgray-100 overflow-y-auto scrollbar pb-4;
@apply fixed top-0 left-0 w-full h-full z-[9999] dark:bg-coolgray-100 bg-white overflow-y-auto scrollbar pb-4;
}
.toast {

View File

@@ -1,18 +1,15 @@
<x-layout-simple>
<div class="flex items-center justify-center h-screen">
<div>
<div class="flex flex-col items-center pb-8">
<a href="{{ route('dashboard') }}">
<div class="text-5xl font-bold tracking-tight text-center dark:text-white">Coolify</div>
</a>
{{-- <x-version /> --}}
<section class="bg-gray-50 dark:bg-base">
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
<a class="flex items-center mb-1 text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
Coolify
</a> <div class="flex items-center gap-2">
{{ __('auth.forgot_password') }}
</div>
<div class="flex items-center gap-2">
<h1>{{ __('auth.forgot_password') }}</h1>
</div>
<div>
@if (is_transactional_emails_active())
<div
class="w-full bg-white shadow md:mt-0 sm:max-w-md xl:p-0 dark:bg-base ">
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
@if (is_transactional_emails_active())
<form action="/forgot-password" method="POST" class="flex flex-col gap-2">
@csrf
<x-forms.input required type="email" name="email" label="{{ __('input.email') }}" autofocus />
@@ -37,7 +34,9 @@
{{ session('status') }}
</div>
@endif
</div>
</div>
</div>
</div>
</section>
</x-layout-simple>

View File

@@ -1,5 +1,14 @@
<nav class="flex flex-col flex-1 pl-2 bg-white border-r dark:border-coolgray-200 dark:bg-base" x-data="{
init() {
switchWidth() {
if (this.full === 'full') {
localStorage.removeItem('pageWidth');
} else {
localStorage.setItem('pageWidth', 'full');
}
window.location.reload();
},
init() {
this.full = localStorage.getItem('pageWidth');
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
const userSettings = localStorage.getItem('theme');
if (userSettings !== 'system') {
@@ -60,13 +69,16 @@
</div>
</x-slot:title>
<div class="flex flex-col gap-1">
<div class="mb-1 font-bold border-b dark:border-coolgray-500 dark:text-white text-md">Color</div>
<button @click="setTheme('dark')" class="px-1 dropdown-item-no-padding">Dark</button>
<button @click="setTheme('light')" class="px-1 dropdown-item-no-padding">Light</button>
<button @click="setTheme('system')" class="px-1 dropdown-item-no-padding">System</button>
<div class="my-1 font-bold border-b dark:border-coolgray-500 dark:text-white text-md">Width</div>
<button @click="switchWidth()" class="px-1 dropdown-item-no-padding" x-show="full">Center</button>
<button @click="switchWidth()" class="px-1 dropdown-item-no-padding" x-show="!full">Full</button>
</div>
</x-dropdown>
</div>
</div>
<div class="px-2 pt-2 pb-7">
<livewire:switch-team />

View File

@@ -6,7 +6,8 @@
x-transition:enter-start="translate-y-full" x-transition:enter-end="translate-y-0"
x-transition:leave="transition ease-in duration-300" x-transition:leave-start="translate-y-0"
x-transition:leave-end="translate-y-full" x-init="setTimeout(() => { bannerVisible = true }, bannerVisibleAfter);"
class="fixed bottom-0 right-0 w-full h-auto duration-300 ease-out sm:px-5 sm:pb-5 sm:w-[26rem] lg:w-full z-[999]" x-cloak>
class="fixed bottom-0 right-0 w-full h-auto duration-300 ease-out sm:px-5 sm:pb-5 sm:w-[26rem] lg:w-full z-[999]"
x-cloak>
<div
class="flex flex-col items-center justify-between w-full h-full max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 dark:bg-coolgray-100 lg:p-8 lg:flex-row sm:rounded">
<div
@@ -27,7 +28,7 @@
@if ($buttonText->attributes->whereStartsWith('@click')->first()) @click="bannerVisible=false;{{ $buttonText->attributes->get('@click') }}"
@else
@click="bannerVisible=false;" @endif
class="inline-flex items-center justify-center flex-shrink-0 w-1/2 px-4 py-2 text-sm font-medium tracking-wide transition-colors duration-200 bg-white rounded-md dark:bg-coolgray-200 lg:w-auto dark:text-neutral-200 dark:hover:bg-coolgray-300 focus:shadow-outline focus:outline-none">
class="inline-flex items-center justify-center flex-shrink-0 w-1/2 px-4 py-2 text-sm font-medium tracking-wide transition-colors duration-200 rounded-md bg-neutral-100 hover:bg-neutral-200 dark:bg-coolgray-200 lg:w-auto dark:text-neutral-200 dark:hover:bg-coolgray-300 focus:shadow-outline focus:outline-none">
{{ $buttonText }}
</button>
</div>

View File

@@ -1,7 +1,7 @@
<div @class([
'transition-all duration-150 box-without-bg dark:bg-coolgray-100 bg-white group border-l-2 border-transparent',
'hover:border-coollabs cursor-pointer' => !$upgrade,
'hover:border-red-500 cursor-not-allowed' => $upgrade,
'transition-all duration-150 box-without-bg dark:bg-coolgray-100 bg-white group',
'hover:border-l-coollabs cursor-pointer' => !$upgrade,
'hover:border-l-red-500 cursor-not-allowed' => $upgrade,
]) @if (!$upgrade) wire:click={{ $wire }} @endif>
<div class="flex items-center">
{{ $logo }}

View File

@@ -1,7 +1,7 @@
<x-emails.layout>
Your server ({{ $name }}) has high disk usage ({{ $disk_usage }}% used). Threshold is {{ $threshold }}%.
Please cleanup your disk to prevent data-loss. Here are some [tips](https://coolify.io/docs/automated-cleanup).
Please cleanup your disk to prevent data-loss. Here are some [tips](https://coolify.io/docs/knowledge-base/server/automated-cleanup).
(You can change the threshold in the Server Settings menu.)
</x-emails.layout>

View File

@@ -5,10 +5,12 @@
<livewire:layout-popups />
@endif
@auth
<livewire:realtime-connection />
@endauth
@auth
<div x-data="{ open: false }" x-cloak class="mx-auto max-w-7xl">
<div x-data="{
open: false,
init() {
this.pageWidth = localStorage.getItem('pageWidth');
}
}" x-cloak class="mx-auto" :class="pageWidth === 'full' ? '' : 'max-w-7xl'">
<div class="relative z-50 lg:hidden" :class="open ? 'block' : 'hidden'" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-black/80"></div>
<div class="fixed inset-0 flex">
@@ -53,7 +55,7 @@
</a> --}}
</div>
<main class="lg:pl-48" >
<main class="lg:pl-48">
<div class="p-4 sm:px-6 lg:px-8 lg:py-6">
{{ $slot }}
</div>

View File

@@ -82,10 +82,12 @@
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
if (element.type === 'password') {
element.type = 'text';
if (element.disabled) return;
element.classList.add('truncate');
this.type = 'text';
} else {
element.type = 'password';
if (element.disabled) return;
element.classList.remove('truncate');
this.type = 'password';
}

View File

@@ -60,7 +60,7 @@
server.
<br />
Check this <a target="_blank" class="underline"
href="https://coolify.io/docs/server/openssh">documentation</a> for further help.
href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="lg:w-64 box-boarding" wire:target="setServerType('localhost')"
wire:click="setServerType('localhost')">Check again

View File

@@ -1,13 +1,52 @@
<div x-data="{
popups: {
sponsorship: true,
notification: true
notification: true,
realtime: false,
},
init() {
this.popups.sponsorship = localStorage.getItem('popupSponsorship') !== 'false';
this.popups.notification = localStorage.getItem('popupNotification') !== 'false';
this.popups.realtime = localStorage.getItem('popupRealtime');
let checkNumber = 1;
let checkPusherInterval = null;
if (!this.popups.realtime) {
checkPusherInterval = setInterval(() => {
if (window.Echo && window.Echo.connector.pusher.connection.state !== 'connected') {
checkNumber++;
if (checkNumber > 4) {
this.popups.realtime = true;
console.error(
'Coolify could not connect to its real-time service. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/knowledge-base/cloudflare/tunnels) or get help on Discord (https://coollabs.io/discord).)'
);
clearInterval(checkPusherInterval);
}
}
}, 1000);
}
}
}">
@auth
<span x-show="popups.realtime === true">
<x-popup>
<x-slot:title>
<span class="font-bold text-left text-red-500">WARNING: </span>Realtime Error?!
</x-slot:title>
<x-slot:description>
<span>Coolify could not connect to its real-time service.<br>This will cause unusual problems on the UI
if
not fixed! <br><br>Please check the
related <a class="underline" href='https://coolify.io/docs/knowledge-base/cloudflare/tunnels'
target='_blank'>documentation</a> or get
help on <a class="underline" href='https://coollabs.io/discord' target='_blank'>Discord</a>. </span>
</x-slot:description>
<x-slot:button-text @click="disableRealtime()">
Acknowledge & Disable This Popup
</x-slot:button-text>
</x-popup>
</span>
@endauth
<span x-show="popups.sponsorship">
<x-popup>
<x-slot:title>
@@ -20,7 +59,8 @@
<x-slot:description>
<span>Please
consider donating on <a href="https://github.com/sponsors/coollabsio"
class="text-xs underline dark:text-white">GitHub</a> or <a href="https://opencollective.com/coollabsio"
class="text-xs underline dark:text-white">GitHub</a> or <a
href="https://opencollective.com/coollabsio"
class="text-xs underline dark:text-white">OpenCollective</a>.<br><br></span>
<span>It enables us to keep creating features without paywalls, ensuring our work remains free and
open.</span>
@@ -35,7 +75,8 @@
<div><span class="font-bold text-red-500">WARNING:</span> The number of active servers exceeds the limit
covered by your payment. If not resolved, some of your servers <span class="font-bold text-red-500">will
be deactivated</span>. Visit <a href="{{ route('subscription.show') }}"
class="underline dark:text-white">/subscription</a> to update your subscription or remove some servers.
class="underline dark:text-white">/subscription</a> to update your subscription or remove some
servers.
</div>
</x-banner>
@endif
@@ -70,8 +111,13 @@
function disableSponsorship() {
localStorage.setItem('popupSponsorship', false);
}
function disableNotification() {
localStorage.setItem('popupNotification', false);
}
function disableRealtime() {
localStorage.setItem('popupRealtime', 'disabled');
}
</script>
</div>

View File

@@ -27,18 +27,17 @@
@endif
@forelse ($deployments as $deployment)
<a @class([
'dark:bg-coolgray-100 p-2 border-l border-dashed transition-colors hover:no-underline',
'dark:bg-coolgray-100 p-2 border-l border-dashed transition-colors hover:no-underline box-without-bg bg-white',
'dark:hover:bg-coolgray-200' =>
data_get($deployment, 'status') === 'queued',
'border-warning hover:bg-warning hover:text-black' =>
data_get($deployment, 'status') === 'in_progress' ||
data_get($deployment, 'status') === 'cancelled-by-user',
'border-error hover:bg-error' =>
'border-error dark:hover:bg-error hover:bg-neutral-200' =>
data_get($deployment, 'status') === 'failed',
'border-success hover:bg-success' =>
'border-success dark:hover:bg-success hover:bg-neutral-200' =>
data_get($deployment, 'status') === 'finished',
]) href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}"
class="hover:no-underline">
]) href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}">
<div class="flex flex-col justify-start">
<div class="flex gap-1">
{{ $deployment->created_at }} UTC

View File

@@ -1,11 +1,45 @@
<div>
<h1 class="py-0">Deployment</h1>
<livewire:project.application.heading :application="$application" />
<div class="pt-4" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }">
<div class="pt-4" x-data="{
fullscreen: false,
alwaysScroll: false,
intervalId: null,
makeFullscreen() {
this.fullscreen = !this.fullscreen;
if (this.fullscreen === false) {
this.alwaysScroll = false;
clearInterval(this.intervalId);
}
},
toggleScroll() {
this.alwaysScroll = !this.alwaysScroll;
if (this.alwaysScroll) {
this.intervalId = setInterval(() => {
const screen = document.getElementById('screen');
const logs = document.getElementById('logs');
if (screen.scrollTop !== logs.scrollHeight) {
screen.scrollTop = logs.scrollHeight;
}
}, 100);
} else {
clearInterval(this.intervalId);
this.intervalId = null;
}
},
goTop() {
this.alwaysScroll = false;
clearInterval(this.intervalId);
const screen = document.getElementById('screen');
screen.scrollTop = 0;
}
}">
<livewire:project.application.deployment-navbar :application_deployment_queue="$application_deployment_queue" />
@if (data_get($application_deployment_queue, 'status') === 'in_progress')
<div class="flex items-center gap-1 pt-2 ">Deployment is
<div class="dark:text-warning"> {{ Str::headline(data_get($this->application_deployment_queue, 'status')) }}.
<div class="dark:text-warning">
{{ Str::headline(data_get($this->application_deployment_queue, 'status')) }}.
</div>
<x-loading class="loading-ring" />
</div>
@@ -17,7 +51,7 @@
@endif
<div id="screen" :class="fullscreen ? 'fullscreen' : ''">
<div @if ($isKeepAliveOn) wire:poll.2000ms="polling" @endif
class="relative flex flex-col-reverse w-full p-2 px-4 mt-4 overflow-y-auto dark:text-white bg-coolgray-100 scrollbar border-coolgray-300"
class="relative flex flex-col-reverse w-full p-2 px-4 mt-4 overflow-y-auto bg-white dark:text-white dark:bg-coolgray-100 scrollbar dark:border-coolgray-300"
:class="fullscreen ? '' : 'max-h-[40rem] border border-dotted rounded'">
<button title="Minimize" x-show="fullscreen" class="fixed top-4 right-4"
x-on:click="makeFullscreen"><svg class="icon" viewBox="0 0 24 24"
@@ -58,7 +92,11 @@
<br>COMMAND: <br>{{ $line['command'] }} <br><br>OUTPUT:
@endif @if (str($line['output'])->contains('http://') || str($line['output'])->contains('https://'))
@php
$line['output'] = preg_replace('/(https?:\/\/[^\s]+)/', '<a href="$1" target="_blank" class="underline text-neutral-400">$1</a>', $line['output']);
$line['output'] = preg_replace(
'/(https?:\/\/[^\s]+)/',
'<a href="$1" target="_blank" class="underline text-neutral-400">$1</a>',
$line['output'],
);
@endphp {!! $line['output'] !!}
@else
{{ $line['output'] }}
@@ -71,39 +109,5 @@
</div>
</div>
</div>
<script>
function makeFullscreen() {
this.fullscreen = !this.fullscreen;
if (this.fullscreen === false) {
this.alwaysScroll = false;
clearInterval(this.intervalId);
}
}
function toggleScroll() {
this.alwaysScroll = !this.alwaysScroll;
if (this.alwaysScroll) {
this.intervalId = setInterval(() => {
const screen = document.getElementById('screen');
const logs = document.getElementById('logs');
if (screen.scrollTop !== logs.scrollHeight) {
screen.scrollTop = logs.scrollHeight;
}
}, 100);
} else {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
function goTop() {
this.alwaysScroll = false;
clearInterval(this.intervalId);
const screen = document.getElementById('screen');
screen.scrollTop = 0;
}
</script>
</div>
</div>

View File

@@ -48,7 +48,7 @@
<div class="w-96">
<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 href='https://coolify.io/docs/docker/compose#raw-docker-compose-deployment'>documentation.</a>" />
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 href='https://coolify.io/docs/knowledge-base/docker/compose#raw-docker-compose-deployment'>documentation.</a>" />
</div>
@if (count($parsedServices) > 0 && !$application->settings->is_raw_compose_deployment_enabled)
<h3>Domains</h3>
@@ -84,13 +84,13 @@
<h3>Docker Registry</h3>
@if ($application->build_pack !== 'dockerimage' && !$application->destination->server->isSwarm())
<x-helper
helper="Push the built image to a docker registry. More info <a class='underline' href='https://coolify.io/docs/docker/registry' target='_blank'>here</a>." />
helper="Push the built image to a docker registry. More info <a class='underline' href='https://coolify.io/docs/knowledge-base/docker/registry' target='_blank'>here</a>." />
@endif
</div>
@if ($application->destination->server->isSwarm())
@if ($application->build_pack !== 'dockerimage')
<div>Docker Swarm requires the image to be available in a registry. More info <a
class="underline" href="https://coolify.io/docs/docker/registry"
class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
target="_blank">here</a>.</div>
@endif
@endif
@@ -132,7 +132,7 @@
@if ($application->build_pack !== 'dockercompose')
<div class="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/server/build-server' class='underline' target='_blank'>documentation</a>."
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>
@@ -148,7 +148,7 @@
id="application.start_command" label="Start Command" />
</div>
<div>Nixpacks will detect the required configuration automatically.
<a class="underline" href="https://coolify.io/docs/frameworks/">Framework Specific Docs</a>
<a class="underline" href="https://coolify.io/docs/resources/introduction">Framework Specific Docs</a>
</div>
@endif
@endif
@@ -206,13 +206,13 @@
know what are
you doing.</div>
<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/custom-docker-options'>docs.</a>"
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
@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/custom-docker-options'>docs.</a>"
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

View File

@@ -57,7 +57,7 @@
<div class="pb-4">Previews</div>
<div class="flex flex-wrap gap-6">
@foreach ($application->previews as $preview)
<div class="flex flex-col p-4 bg-coolgray-200">
<div class="flex flex-col p-4 dark:bg-coolgray-200">
<div class="flex gap-2">PR #{{ data_get($preview, 'pull_request_id') }} |
@if (Str::of(data_get($preview, 'status'))->startsWith('running'))
<x-status.running :status="data_get($preview, 'status')" />
@@ -78,7 +78,7 @@
</a>
</div>
<div class="flex items-center gap-2 pt-6">
<x-forms.button class="bg-coolgray-500"
<x-forms.button class="dark:bg-coolgray-500"
wire:click="deploy({{ data_get($preview, 'pull_request_id') }})">
@if (data_get($preview, 'status') === 'exited')
Deploy
@@ -88,17 +88,17 @@
</x-forms.button>
<a
href="{{ route('project.application.deployment.index', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
<x-forms.button class="bg-coolgray-500">
<x-forms.button class="dark:bg-coolgray-500">
Deployment Logs
</x-forms.button>
</a>
<a
href="{{ route('project.application.logs', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
<x-forms.button class="bg-coolgray-500">
<x-forms.button class="dark:bg-coolgray-500">
Application Logs
</x-forms.button>
</a>
<x-forms.button isError class="bg-coolgray-500"
<x-forms.button isError class="dark:bg-coolgray-500"
wire:click="stop({{ data_get($preview, 'pull_request_id') }})">Delete
</x-forms.button>
</div>

View File

@@ -15,8 +15,8 @@
<div class="pt-4 pb-2">Docker Networks</div>
<div class="grid grid-cols-1 gap-2 pb-4 lg:grid-cols-4">
@foreach ($server->destinations() as $destination)
<div class="cursor-pointer box-without-bg bg-coolgray-200 group"
:class="'{{ $selectedDestination === $destination->id }}' && 'bg-coollabs dark:text-white'"
<div class="cursor-pointer box-without-bg group"
:class="'{{ $selectedDestination === $destination->id }}' ? 'bg-coollabs text-white' : 'dark:bg-coolgray-100 bg-white'"
wire:click="selectServer('{{ $server->id }}', '{{ $destination->id }}')">
{{ $destination->name }}
</div>
@@ -30,7 +30,7 @@
<div>These will be cloned to the new project</div>
<div class="grid grid-cols-1 gap-2 pt-4 opacity-95 lg:grid-cols-2 xl:grid-cols-3">
@foreach ($environment->applications->sortBy('name') as $application)
<div class="cursor-default box-without-bg bg-coolgray-100 group">
<div class="bg-white cursor-default box-without-bg dark:bg-coolgray-100 group">
<div class="flex flex-col">
<div class="font-bold dark:text-white">{{ $application->name }}</div>
<div class="description">{{ $application->description }}</div>
@@ -38,7 +38,7 @@
</div>
@endforeach
@foreach ($environment->databases()->sortBy('name') as $database)
<div class="cursor-default box-without-bg bg-coolgray-100 group">
<div class="bg-white cursor-default box-without-bg dark:bg-coolgray-100 group">
<div class="flex flex-col">
<div class="font-bold dark:text-white">{{ $database->name }}</div>
<div class="description">{{ $database->description }}</div>
@@ -46,7 +46,7 @@
</div>
@endforeach
@foreach ($environment->services->sortBy('name') as $service)
<div class="cursor-default box-without-bg bg-coolgray-100 group">
<div class="bg-white cursor-default box-without-bg dark:bg-coolgray-100 group">
<div class="flex flex-col">
<div class="font-bold dark:text-white">{{ $service->name }}</div>
<div class="description">{{ $service->description }}</div>

View File

@@ -11,14 +11,16 @@
</a>
</div>
@else
<div @class([
'border-coollabs' =>
data_get($backup, 'id') === data_get($selectedBackup, 'id'),
'flex flex-col box border-l-2 border-transparent',
]) wire:click="setSelectedBackup('{{ data_get($backup, 'id') }}')">
<div>Frequency: {{ $backup->frequency }}</div>
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
<div>Number of backups to keep (locally): {{ $backup->number_of_backups_locally }}</div>
<div class="box">
<div @class([
'border-coollabs' =>
data_get($backup, 'id') === data_get($selectedBackup, 'id'),
'flex flex-col border-l-2 border-transparent',
]) wire:click="setSelectedBackup('{{ data_get($backup, 'id') }}')">
<div>Frequency: {{ $backup->frequency }}</div>
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
<div>Number of backups to keep (locally): {{ $backup->number_of_backups_locally }}</div>
</div>
</div>
@endif
@empty

View File

@@ -24,7 +24,7 @@
<div>You can use these variables anywhere with</div>
<div class=" dark:text-warning text-coollabs">@{{ project.VARIABLENAME }} </div>
<x-helper
helper="More info <a class='underline dark:text-white' href='https://coolify.io/docs/environment-variables#shared-variables' target='_blank'>here</a>."></x-helper>
helper="More info <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/environment-variables#shared-variables' target='_blank'>here</a>."></x-helper>
</div>
<div class="flex flex-col gap-2">
@forelse ($project->environment_variables->sort()->sortBy('real_value') as $env)

View File

@@ -49,7 +49,7 @@
</x-modal-input>
</div>
<div class="flex items-center gap-2 pb-4">You can use these variables anywhere with <span class="dark:text-warning text-coollabs">@{{environment.VARIABLENAME}}</span><x-helper
helper="More info <a class='underline dark:text-white' href='https://coolify.io/docs/environment-variables#shared-variables' target='_blank'>here</a>."></x-helper>
helper="More info <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/environment-variables#shared-variables' target='_blank'>here</a>."></x-helper>
</div>
<div class="flex flex-col gap-2">
@forelse ($environment->environment_variables->sort()->sortBy('real_value') as $env)

File diff suppressed because one or more lines are too long

View File

@@ -72,10 +72,10 @@
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
<template x-for="tag in item.tags">
<div class="px-2 py-1 cursor-pointer box-description dark:bg-coolgray-100 dark:hover:bg-coolgray-300 bg-neutral-100 hover:bg-neutral-200"
<div class="tag"
@click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="flex items-center px-2 text-xs cursor-pointer dark:text-neutral-500/20 text-neutral-500 group-hover:text-neutral-700 group-hover:dark:text-white dark:hover:bg-coolgray-300 hover:bg-neutral-200"
<div class="add-tag"
@click.prevent="goto(item)">Add tag</div>
</div>
</span>
@@ -104,10 +104,10 @@
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
<template x-for="tag in item.tags">
<div class="px-2 py-1 cursor-pointer description bg-coolgray-100 hover:bg-coolgray-300"
<div class="tag"
@click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="flex items-center px-2 text-xs cursor-pointer text-neutral-500/20 group-hover:dark:text-white hover:bg-coolgray-300"
<div class="add-tag"
@click.prevent="goto(item)">Add tag</div>
</div>
</span>
@@ -136,10 +136,10 @@
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="px-2 py-1 cursor-pointer description bg-coolgray-100 hover:bg-coolgray-300"
<div class="tag"
@click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="flex items-center px-2 text-xs cursor-pointer text-neutral-500/20 group-hover:dark:text-white hover:bg-coolgray-300"
<div class="add-tag"
@click.prevent="goto(item)">Add tag</div>
</div>
</span>
@@ -168,10 +168,10 @@
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="px-2 py-1 cursor-pointer description bg-coolgray-100 hover:bg-coolgray-300"
<div class="tag"
@click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="flex items-center px-2 text-xs cursor-pointer text-neutral-500/20 group-hover:dark:text-white hover:bg-coolgray-300"
<div class="add-tag"
@click.prevent="goto(item)">Add tag</div>
</div>
</span>
@@ -200,10 +200,10 @@
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="px-2 py-1 cursor-pointer description bg-coolgray-100 hover:bg-coolgray-300"
<div class="tag"
@click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="flex items-center px-2 text-xs cursor-pointer text-neutral-500/20 group-hover:dark:text-white hover:bg-coolgray-300"
<div class="add-tag"
@click.prevent="goto(item)">Add tag</div>
</div>
</span>
@@ -232,10 +232,10 @@
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="px-2 py-1 cursor-pointer description bg-coolgray-100 hover:bg-coolgray-300"
<div class="tag"
@click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="flex items-center px-2 text-xs cursor-pointer text-neutral-500/20 group-hover:dark:text-white hover:bg-coolgray-300"
<div class="add-tag"
@click.prevent="goto(item)">Add tag</div>
</div>
</span>
@@ -264,10 +264,10 @@
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="px-2 py-1 cursor-pointer description bg-coolgray-100 hover:bg-coolgray-300"
<div class="tag"
@click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="flex items-center px-2 text-xs cursor-pointer text-neutral-500/20 group-hover:dark:text-white hover:bg-coolgray-300"
<div class="add-tag"
@click.prevent="goto(item)">Add tag</div>
</div>
</span>

View File

@@ -51,7 +51,7 @@
$application->status)->contains(['running']),
'border-l border-dashed border-warning' => Str::of(
$application->status)->contains(['starting']),
'flex gap-2 box-without-bg bg-coolgray-100 hover:text-neutral-300 group',
'flex gap-2 box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-300 group',
])>
<div class="flex flex-row w-full">
<div class="flex flex-col flex-1">
@@ -75,7 +75,7 @@
<div class="text-xs">{{ $application->status }}</div>
</div>
<div class="flex items-center px-4">
<a class="mx-4 font-bold hover:underline"
<a class="mx-4 text-xs font-bold hover:underline"
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $application->uuid]) }}">
Settings
</a>
@@ -95,7 +95,7 @@
$database->status)->contains(['running']),
'border-l border-dashed border-warning' => Str::of(
$database->status)->contains(['restarting']),
'flex gap-2 box-without-bg bg-coolgray-100 hover:text-neutral-300 group',
'flex gap-2 box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-300 group',
])>
<div class="flex flex-row w-full">
<div class="flex flex-col flex-1">
@@ -116,7 +116,7 @@
<div class="text-xs">{{ $database->status }}</div>
</div>
<div class="flex items-center px-4">
<a class="mx-4 font-bold hover:underline"
<a class="mx-4 text-xs font-bold hover:underline"
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $database->uuid]) }}">
Settings
</a>

View File

@@ -17,10 +17,9 @@
@click.prevent="activeTab = 'scheduled-tasks'; window.location.hash = 'scheduled-tasks'"
href="#">Scheduled Tasks
</a>
@if (
$serviceDatabase?->databaseType() === 'standalone-mysql' ||
$serviceDatabase?->databaseType() === 'standalone-postgresql' ||
$serviceDatabase?->databaseType() === 'standalone-mariadb')
@if (str($serviceDatabase?->databaseType())->contains('mysql') ||
str($serviceDatabase?->databaseType())->contains('postgres') ||
str($serviceDatabase?->databaseType())->contains('mariadb'))
<a :class="activeTab === 'backups' && 'dark:text-white'"
@click.prevent="activeTab = 'backups'; window.location.hash = 'backups'" href="#">Backups</a>
@endif

View File

@@ -15,7 +15,7 @@
</div>
<div class="w-96">
<x-forms.checkbox instantSave id="service.connect_to_docker_network" label="Connect To Predefined Network"
helper="By default, you do not reach the Coolify defined networks.<br>Starting a docker compose based resource will have an internal network. <br>If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.<br><br>For more information, check <a class='underline dark:text-white' target='_blank' href='https://coolify.io/docs/docker/compose#connect-to-predefined-networks'>this</a>." />
helper="By default, you do not reach the Coolify defined networks.<br>Starting a docker compose based resource will have an internal network. <br>If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.<br><br>For more information, check <a class='underline dark:text-white' target='_blank' href='https://coolify.io/docs/knowledge-base/docker/compose#connect-to-predefined-networks'>this</a>." />
</div>
@if ($fields)
<div>

View File

@@ -1,6 +1,6 @@
<div>
<form wire:submit='submit'
class="flex flex-col items-center gap-4 p-4 bg-white border lg:items-start dark:border-coolgray-300">
class="flex flex-col items-center gap-4 p-4 bg-white border lg:items-start dark:bg-base dark:border-coolgray-300">
@if ($isLocked)
<div class="flex flex-1 w-full gap-2">
<x-forms.input disabled id="env.key" />
@@ -46,8 +46,8 @@
@if ($env->is_shared)
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
@else
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
@endif
@endif
<div class="flex-1"></div>

View File

@@ -1,5 +1,38 @@
<div>
<div x-init="$wire.getLogs" id="screen" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }">
<div x-init="$wire.getLogs" id="screen" x-data="{
fullscreen: false,
alwaysScroll: false,
intervalId: null,
makeFullscreen() {
this.fullscreen = !this.fullscreen;
if (this.fullscreen === false) {
this.alwaysScroll = false;
clearInterval(this.intervalId);
}
},
toggleScroll() {
this.alwaysScroll = !this.alwaysScroll;
if (this.alwaysScroll) {
this.intervalId = setInterval(() => {
const screen = document.getElementById('screen');
const logs = document.getElementById('logs');
if (screen.scrollTop !== logs.scrollHeight) {
screen.scrollTop = logs.scrollHeight;
}
}, 100);
} else {
clearInterval(this.intervalId);
this.intervalId = null;
}
},
goTop() {
this.alwaysScroll = false;
clearInterval(this.intervalId);
const screen = document.getElementById('screen');
screen.scrollTop = 0;
}
}">
<div class="flex items-center gap-2">
@if ($resource?->type() === 'application')
<h3>{{ $container }}</h3>
@@ -24,7 +57,7 @@
<x-forms.checkbox instantSave label="Include Timestamps" id="showTimeStamps"></x-forms.checkbox>
</form>
<div :class="fullscreen ? 'fullscreen' : 'relative w-full py-4 mx-auto'">
<div class="flex flex-col-reverse w-full px-4 py-2 overflow-y-auto dark:text-white bg-coolgray-100 scrollbar border-coolgray-300"
<div class="flex flex-col-reverse w-full px-4 py-2 overflow-y-auto bg-white dark:text-white dark:bg-coolgray-100 scrollbar dark:border-coolgray-300"
:class="fullscreen ? '' : 'max-h-96 border border-solid rounded'">
<button title="Minimize" x-show="fullscreen" class="fixed top-4 right-4"
x-on:click="makeFullscreen"><svg class="icon" viewBox="0 0 24 24"
@@ -61,39 +94,5 @@
@endif
</div>
</div>
<script>
function makeFullscreen() {
this.fullscreen = !this.fullscreen;
if (this.fullscreen === false) {
this.alwaysScroll = false;
clearInterval(this.intervalId);
}
}
function toggleScroll() {
this.alwaysScroll = !this.alwaysScroll;
if (this.alwaysScroll) {
this.intervalId = setInterval(() => {
const screen = document.getElementById('screen');
const logs = document.getElementById('logs');
if (screen.scrollTop !== logs.scrollHeight) {
screen.scrollTop = logs.scrollHeight;
}
}, 100);
} else {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
function goTop() {
this.alwaysScroll = false;
clearInterval(this.intervalId);
const screen = document.getElementById('screen');
screen.scrollTop = 0;
}
</script>
</div>
</div>

View File

@@ -2,11 +2,11 @@
<div class="flex items-center gap-2">
<h2>Webhooks</h2>
<x-helper
helper="For more details goto our <a class='dark:text-white underline' href='https://coolify.io/docs/api/deploy-webhook' target='_blank'>docs</a>." />
helper="For more details goto our <a class='underline dark:text-white' href='https://coolify.io/docs/api-reference/deploy-webhook' target='_blank'>docs</a>." />
</div>
<div>
<x-forms.input readonly
helper="See details in our <a target='_blank' class='dark:text-white underline' href='https://coolify.io/docs/api/authentication'>documentation</a>."
helper="See details in our <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/api-reference/deploy-webhook'>documentation</a>."
label="Deploy Webhook (auth required)" id="deploywebhook"></x-forms.input>
</div>
@if ($resource->type() === 'application')

View File

@@ -1,48 +0,0 @@
<div x-data="{ showNotification: @entangle('showNotification') }">
@if ($checkConnection)
@script
<script>
let checkPusherInterval = null;
let checkNumber = 0;
checkPusherInterval = setInterval(() => {
if (window.Echo) {
if (window.Echo.connector.pusher.connection.state !== 'connected') {
checkNumber++;
if (checkNumber > 4) {
@if ($isNotificationEnabled)
$wire.showNotification = true;
@endif
console.error(
'Coolify could not connect to the new realtime service introduced in beta.154. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/cloudflare/tunnels) or get help on Discord (https://coollabs.io/discord).)'
);
clearInterval(checkPusherInterval);
}
} else {
console.log('Coolify Realtime Service is connected!');
clearInterval(checkPusherInterval);
}
} else {
@if ($isNotificationEnabled)
$wire.showNotification = true;
@endif
console.error(
'Coolify could not connect to the new realtime service introduced in beta.154. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/cloudflare/tunnels) or get help on Discord (https://coollabs.io/discord).)'
);
clearInterval(checkPusherInterval);
}
}, 1000);
</script>
@endscript
<div class="toast z-[9999]" x-cloak x-show="showNotification">
<div class="flex flex-col dark:text-white border border-red-500 border-dashed rounded alert-error bg-coolgray-200">
<span><span class="font-bold text-left text-red-500">WARNING: </span>Coolify could not connect to the new
realtime service introduced in beta.154. <br>This will cause unusual problems on the UI if not
fixed!<br><br>Please check the
related <a href='https://coolify.io/docs/cloudflare/tunnels' target='_blank'>documentation</a> or get
help on <a href='https://coollabs.io/discord' target='_blank'>Discord</a>.</span>
<x-forms.button class="bg-coolgray-400" wire:click='disable'>Acknowledge the problem and disable this
popup</x-forms.button>
</div>
</div>
@endif
</div>

View File

@@ -83,20 +83,20 @@
@endif
@if ($server->settings->is_swarm_worker)
<x-forms.checkbox disabled instantSave type="checkbox" id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/docker/swarm' target='_blank'>here</a>."
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/docker/swarm' target='_blank'>here</a>."
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@endif
@if ($server->settings->is_swarm_manager)
<x-forms.checkbox disabled instantSave type="checkbox" id="server.settings.is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/docker/swarm' target='_blank'>here</a>."
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/docker/swarm' target='_blank'>here</a>."
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@endif
@endif

View File

@@ -29,24 +29,24 @@
<div class="">
<h3 class="pt-6">Swarm <span class="text-xs text-neutral-500">(experimental)</span></h3>
<div class="pb-4">Read the docs <a class='dark:text-white'
href='https://coolify.io/docs/docker/swarm#deploy-with-persistent-storage'
href='https://coolify.io/docs/knowledge-base/docker/swarm'
target='_blank'>here</a>.</div>
@if ($is_swarm_worker || $is_build_server)
<x-forms.checkbox disabled instantSave type="checkbox" id="is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/docker/swarm' target='_blank'>here</a>."
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@else
<x-forms.checkbox type="checkbox" instantSave id="is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/docker/swarm' target='_blank'>here</a>."
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@endif
@if ($is_swarm_manager|| $is_build_server)
<x-forms.checkbox disabled instantSave type="checkbox" id="is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/docker/swarm' target='_blank'>here</a>."
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@else
<x-forms.checkbox type="checkbox" instantSave id="is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/docker/swarm' target='_blank'>here</a>."
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@endif
@if ($is_swarm_worker && count($swarm_managers) > 0)

View File

@@ -17,8 +17,8 @@
xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16" />
</svg>Before switching proxies, please read <a class="dark:text-white underline"
href="https://coolify.io/docs/server/switching-proxies">this</a>.</div>
</svg>Before switching proxies, please read <a class="underline dark:text-white"
href="https://coolify.io/docs/knowledge-base/server/proxies#switch-between-proxies">this</a>.</div>
@if ($server->proxyType() === 'TRAEFIK_V2')
<div class="pb-4">Traefik v2</div>
@elseif ($server->proxyType() === 'CADDY')

View File

@@ -2,11 +2,7 @@
<div class="flex items-center gap-2">
<h2>Transactional Email</h2>
</div>
@if (isCloud())
<div class="pb-4 ">Email settings for password resets, invitations, shared with Pro+ subscribers etc.</div>
@else
<div class="pb-4 ">Email settings for password resets, invitations, etc.</div>
@endif
<div class="pb-4 ">Email settings for password resets, invitations, etc.</div>
<form wire:submit='submitFromFields' class="flex flex-col gap-2 pb-4">
<x-forms.input required id="settings.smtp_from_name" helper="Name used in emails." label="From Name" />
<x-forms.input required id="settings.smtp_from_address" helper="Email address used in emails."

View File

@@ -8,7 +8,7 @@
</div>
<div class="flex items-center gap-2 pb-4">You can use these variables anywhere with <span
class="dark:text-warning text-coollabs">@{{ team.VARIABLENAME }}</span> <x-helper
helper="More info <a class='underline dark:text-white' href='https://coolify.io/docs/environment-variables#shared-variables' target='_blank'>here</a>."></x-helper>
helper="More info <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/environment-variables#shared-variables' target='_blank'>here</a>."></x-helper>
</div>
<div class="flex flex-col gap-2">

View File

@@ -1,10 +1,7 @@
{
"coolify": {
"main": {
"version": "3.12.36"
},
"v4": {
"version": "4.0.0-beta.244"
"version": "4.0.0-beta.246"
}
}
}