Merge branch 'next' into feat/disable-default-redirect

This commit is contained in:
Kael
2024-10-14 22:53:37 +11:00
8 changed files with 191 additions and 24 deletions

View File

@@ -38,7 +38,7 @@ class Kernel extends ConsoleKernel
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
// Server Jobs
$this->check_scheduled_backups($schedule);
$this->check_resources($schedule);
// $this->check_resources($schedule);
$this->check_scheduled_tasks($schedule);
$schedule->command('uploads:clear')->everyTwoMinutes();

View File

@@ -2,14 +2,16 @@
namespace App\Jobs;
use App\Actions\Proxy\StartProxy;
use App\Models\Application;
use App\Models\ApplicationPreview;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class PushServerUpdateJob implements ShouldQueue
@@ -25,11 +27,19 @@ class PushServerUpdateJob implements ShouldQueue
return isDev() ? 1 : 3;
}
public function __construct(public Server $server, public $data) {}
public function __construct(public Server $server, public $data)
{
// TODO: Handle multiple servers
// TODO: Handle Preview deployments
// TODO: Handle DB TCP proxies
// TODO: Handle DBs
// TODO: Handle services
// TODO: Handle proxies
}
public function handle()
{
if (!$this->data) {
if (! $this->data) {
throw new \Exception('No data provided');
}
$data = collect($this->data);
@@ -37,17 +47,90 @@ class PushServerUpdateJob implements ShouldQueue
if ($containers->isEmpty()) {
return;
}
$foundApplicationIds = collect();
$foundServiceIds = collect();
$foundProxy = false;
foreach ($containers as $container) {
$containerStatus = data_get($container, 'status', 'exited');
$containerHealth = data_get($container, 'health', 'unhealthy');
$containerStatus = data_get($container, 'state', 'exited');
$containerHealth = data_get($container, 'health_status', 'unhealthy');
$containerStatus = "$containerStatus ($containerHealth)";
$labels = collect(data_get($container, 'labels'));
if ($labels->has('coolify.applicationId')) {
$applicationId = $labels->get('coolify.applicationId');
$coolify_managed = $labels->has('coolify.managed');
if ($coolify_managed) {
if ($labels->has('coolify.applicationId')) {
$applicationId = $labels->get('coolify.applicationId');
$pullRequestId = data_get($labels, 'coolify.pullRequestId', '0');
$foundApplicationIds->push($applicationId);
try {
$this->updateApplicationStatus($applicationId, $pullRequestId, $containerStatus);
} catch (\Exception $e) {
Log::error($e);
}
} elseif ($labels->has('coolify.serviceId')) {
$serviceId = $labels->get('coolify.serviceId');
$foundServiceIds->push($serviceId);
Log::info("Service: $serviceId, $containerStatus");
} else {
$name = data_get($container, 'name');
$uuid = $labels->get('com.docker.compose.service');
$type = $labels->get('coolify.type');
if ($name === 'coolify-proxy') {
$foundProxy = true;
Log::info("Proxy: $uuid, $containerStatus");
} elseif ($type === 'service') {
Log::info("Service: $uuid, $containerStatus");
} else {
Log::info("Database: $uuid, $containerStatus");
}
}
}
Log::info("$applicationId, $containerStatus");
}
// If proxy is not found, start it
if (! $foundProxy && $this->server->isProxyShouldRun()) {
Log::info('Proxy not found, starting it');
StartProxy::dispatch($this->server);
}
// Update not found applications
$allApplicationIds = $this->server->applications()->pluck('id');
$notFoundApplicationIds = $allApplicationIds->diff($foundApplicationIds);
if ($notFoundApplicationIds->isNotEmpty()) {
Log::info('Not found application ids', ['application_ids' => $notFoundApplicationIds]);
$this->updateNotFoundApplications($notFoundApplicationIds);
}
}
private function updateApplicationStatus(string $applicationId, string $pullRequestId, string $containerStatus)
{
if ($pullRequestId === '0') {
$application = Application::find($applicationId);
if (! $application) {
return;
}
$application->status = $containerStatus;
$application->save();
Log::info('Application updated', ['application_id' => $applicationId, 'status' => $containerStatus]);
} else {
$application = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
if (! $application) {
return;
}
$application->status = $containerStatus;
$application->save();
}
}
private function updateNotFoundApplications(Collection $applicationIds)
{
$applicationIds->each(function ($applicationId) {
Log::info('Updating application status', ['application_id' => $applicationId, 'status' => 'exited']);
$application = Application::find($applicationId);
if ($application) {
$application->status = 'exited';
$application->save();
Log::info('Application status updated', ['application_id' => $applicationId, 'status' => 'exited']);
}
});
}
}

View File

@@ -17,8 +17,6 @@ use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml;
use Illuminate\Support\Str;
#[OA\Schema(
description: 'Server model',
@@ -177,7 +175,7 @@ class Server extends BaseModel
public function setupDefaultRedirect()
{
$banner =
"# This file is generated by Coolify, do not edit it manually.\n" .
"# This file is generated by Coolify, do not edit it manually.\n".
"# Disable the default redirect to customize (only if you know what are you doing).\n\n";
$dynamic_conf_path = $this->proxyPath() . '/dynamic';
$proxy_type = $this->proxyType();
@@ -254,7 +252,7 @@ class Server extends BaseModel
}
$conf = Yaml::dump($dynamic_conf, 12, 2);
}
$conf = $banner . $conf;
$conf = $banner.$conf;
$base64 = base64_encode($conf);
instant_remote_process([
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
@@ -269,7 +267,7 @@ class Server extends BaseModel
public function setupDynamicProxyConfiguration()
{
$settings = instanceSettings();
$dynamic_config_path = $this->proxyPath() . '/dynamic';
$dynamic_config_path = $this->proxyPath().'/dynamic';
if ($this->proxyType() === ProxyTypes::TRAEFIK->value) {
$file = "$dynamic_config_path/coolify.yaml";
if (empty($settings->fqdn) || (isCloud() && $this->id !== 0) || ! $this->isLocalhost()) {
@@ -388,8 +386,8 @@ class Server extends BaseModel
}
$yaml = Yaml::dump($traefik_dynamic_conf, 12, 2);
$yaml =
"# This file is automatically generated by Coolify.\n" .
"# Do not edit it manually (only if you know what are you doing).\n\n" .
"# This file is automatically generated by Coolify.\n".
"# Do not edit it manually (only if you know what are you doing).\n\n".
$yaml;
$base64 = base64_encode($yaml);
@@ -453,13 +451,13 @@ $schema://$host {
if (isDev()) {
$proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/caddy';
} else {
$proxy_path = $proxy_path . '/caddy';
$proxy_path = $proxy_path.'/caddy';
}
} elseif ($proxyType === ProxyTypes::NGINX->value) {
if (isDev()) {
$proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/nginx';
} else {
$proxy_path = $proxy_path . '/nginx';
$proxy_path = $proxy_path.'/nginx';
}
}
@@ -531,8 +529,10 @@ $schema://$host {
$encrypted = encrypt($token);
$this->settings->sentinel_token = $encrypted;
$this->settings->save();
return $encrypted;
}
public function isSentinelEnabled()
{
return $this->isMetricsEnabled() || $this->isServerApiEnabled();
@@ -984,7 +984,8 @@ $schema://$host {
public function isProxyShouldRun()
{
if ($this->proxyType() === ProxyTypes::NONE->value || $this->settings->is_build_server) {
// TODO: Do we need "|| $this->proxy->force_stop" here?
if ($this->proxyType() === ProxyTypes::NONE->value || $this->isBuildServer()) {
return false;
}

View File

@@ -19,6 +19,9 @@ return new class extends Migration
$table->integer('sentinel_metrics_refresh_rate_seconds')->default(5);
$table->integer('sentinel_metrics_history_days')->default(30);
});
Schema::table('servers', function (Blueprint $table) {
$table->dateTime('sentinel_update_at')->default(now());
});
}
/**
@@ -34,5 +37,8 @@ return new class extends Migration
$table->dropColumn('sentinel_metrics_refresh_rate_seconds');
$table->dropColumn('sentinel_metrics_history_days');
});
Schema::table('servers', function (Blueprint $table) {
$table->dropColumn('sentinel_update_at');
});
}
};

9
public/svgs/calcom.svg Normal file
View File

@@ -0,0 +1,9 @@
<svg width="101" height="22" viewBox="0 0 101 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0582 20.817C4.32115 20.817 0 16.2763 0 10.6704C0 5.04589 4.1005 0.467773 10.0582 0.467773C13.2209 0.467773 15.409 1.43945 17.1191 3.66311L14.3609 5.96151C13.2025 4.72822 11.805 4.11158 10.0582 4.11158C6.17833 4.11158 4.04533 7.08268 4.04533 10.6704C4.04533 14.2582 6.38059 17.1732 10.0582 17.1732C11.7866 17.1732 13.2577 16.5566 14.4161 15.3233L17.1375 17.7151C15.501 19.8453 13.2577 20.817 10.0582 20.817Z" fill="#fafafa"/>
<path d="M29.0161 5.88601H32.7304V20.4612H29.0161V18.331C28.2438 19.8446 26.9566 20.8536 24.4927 20.8536C20.5577 20.8536 17.4133 17.4341 17.4133 13.2297C17.4133 9.02528 20.5577 5.60571 24.4927 5.60571C26.9383 5.60571 28.2438 6.61477 29.0161 8.12835V5.88601ZM29.1264 13.2297C29.1264 10.95 27.5634 9.06266 25.0995 9.06266C22.7274 9.06266 21.1828 10.9686 21.1828 13.2297C21.1828 15.4346 22.7274 17.3967 25.0995 17.3967C27.5451 17.3967 29.1264 15.4907 29.1264 13.2297Z" fill="#fafafa"/>
<path d="M35.3599 0H39.0742V20.4427H35.3599V0Z" fill="#fafafa"/>
<path d="M40.7291 18.5182C40.7291 17.3223 41.6853 16.3132 42.9908 16.3132C44.2964 16.3132 45.2158 17.3223 45.2158 18.5182C45.2158 19.7515 44.278 20.7605 42.9908 20.7605C41.7037 20.7605 40.7291 19.7515 40.7291 18.5182Z" fill="#fafafa"/>
<path d="M59.4296 18.1068C58.0505 19.7885 55.9543 20.8536 53.4719 20.8536C49.0404 20.8536 45.7858 17.4341 45.7858 13.2297C45.7858 9.02528 49.0404 5.60571 53.4719 5.60571C55.8623 5.60571 57.9402 6.61477 59.3193 8.20309L56.4508 10.6136C55.7336 9.71667 54.7958 9.04397 53.4719 9.04397C51.0999 9.04397 49.5553 10.95 49.5553 13.211C49.5553 15.472 51.0999 17.378 53.4719 17.378C54.9062 17.378 55.8991 16.6306 56.6346 15.6215L59.4296 18.1068Z" fill="#fafafa"/>
<path d="M59.7422 13.2297C59.7422 9.02528 62.9968 5.60571 67.4283 5.60571C71.8598 5.60571 75.1144 9.02528 75.1144 13.2297C75.1144 17.4341 71.8598 20.8536 67.4283 20.8536C62.9968 20.8349 59.7422 17.4341 59.7422 13.2297ZM71.3449 13.2297C71.3449 10.95 69.8003 9.06266 67.4283 9.06266C65.0563 9.04397 63.5117 10.95 63.5117 13.2297C63.5117 15.4907 65.0563 17.3967 67.4283 17.3967C69.8003 17.3967 71.3449 15.4907 71.3449 13.2297Z" fill="#fafafa"/>
<path d="M100.232 11.5482V20.4428H96.518V12.4638C96.518 9.94119 95.3412 8.85739 93.576 8.85739C91.921 8.85739 90.7442 9.67958 90.7442 12.4638V20.4428H87.0299V12.4638C87.0299 9.94119 85.8346 8.85739 84.0878 8.85739C82.4329 8.85739 80.9802 9.67958 80.9802 12.4638V20.4428H77.2659V5.8676H80.9802V7.88571C81.7525 6.31607 83.15 5.53125 85.3014 5.53125C87.3425 5.53125 89.0525 6.5403 89.9903 8.24074C90.9281 6.50293 92.3072 5.53125 94.8079 5.53125C97.8603 5.54994 100.232 7.86702 100.232 11.5482Z" fill="#fafafa"/>
<script xmlns=""/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -282,7 +282,7 @@
{{-- @endif --}}
</div>
@if (isDev())
<x-forms.button wire:click="getPushData">Get Push Data</x-forms.button>
<x-forms.button wire:click="getPushData"> Push Test </x-forms.button>
{{-- <div class="w-64">
<x-forms.checkbox instantSave id="server.settings.is_metrics_enabled" label="Enable Metrics" />
<x-forms.button>Start Sentinel</x-forms.button>

View File

@@ -15,7 +15,6 @@ use App\Http\Middleware\IgnoreReadOnlyApiToken;
use App\Http\Middleware\OnlyRootApiToken;
use App\Jobs\PushServerUpdateJob;
use App\Models\Server;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
Route::get('/health', [OtherController::class, 'healthcheck']);
@@ -137,7 +136,7 @@ Route::group([
], function () {
Route::post('/sentinel/push', function () {
$token = request()->header('Authorization');
if (!$token) {
if (! $token) {
return response()->json(['message' => 'Unauthorized'], 401);
}
$naked_token = str_replace('Bearer ', '', $token);
@@ -145,11 +144,13 @@ Route::group([
$decrypted_token = json_decode($decrypted, true);
$server_uuid = data_get($decrypted_token, 'server_uuid');
$server = Server::where('uuid', $server_uuid)->first();
if (!$server) {
if (! $server) {
return response()->json(['message' => 'Server not found'], 404);
}
$data = request()->all();
$server->update(['sentinel_update_at' => now()]);
PushServerUpdateJob::dispatch($server, $data);
return response()->json(['message' => 'ok'], 200);
});
});

View File

@@ -0,0 +1,67 @@
# documentation: https://cal.com/docs
# slogan: Scheduling infrastructure for everyone.
# tags: calcom,calendso,scheduling,open,source
# logo: svgs/calcom.svg
# port: 3000
services:
calcom:
image: calcom.docker.scarf.sh/calcom/cal.com
environment:
# Some variables still uses Calcom previous name, Calendso
#
# Full list https://github.com/calcom/cal.com/blob/main/.env.example
- SERVICE_FQDN_CALCOM_3000
- NEXT_PUBLIC_LICENSE_CONSENT=agree
- NODE_ENV=production
- NEXT_PUBLIC_WEBAPP_URL=${SERVICE_FQDN_CALCOM}
- NEXT_PUBLIC_API_V2_URL=${SERVICE_FQDN_CALCOM}/api/v2
# https://next-auth.js.org/configuration/options#nextauth_url
# From https://github.com/calcom/docker?tab=readme-ov-file#important-run-time-variables, it should be ${NEXT_PUBLIC_WEBAPP_URL}/api/auth
- NEXTAUTH_URL=${SERVICE_FQDN_CALCOM}/api/auth
# It is highly recommended that the NEXTAUTH_SECRET must be overridden and very unique
# Use `openssl rand -base64 32` to generate a key
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET:-$SERVICE_BASE64_CALCOM_SECRET}
# Encryption key that will be used to encrypt CalDAV credentials, choose a random string, for example with `dd if=/dev/urandom bs=1K count=1 | md5sum`
- CALENDSO_ENCRYPTION_KEY=${CALENDSO_ENCRYPTION_KEY:-$SERVICE_BASE64_CALCOM_KEY}
- POSTGRES_USER=${SERVICE_USER_POSTGRES}
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
- POSTGRES_DB=${POSTGRES_DB:-calendso}
- DATABASE_HOST=postgresql
- DATABASE_URL=postgresql://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@${DATABASE_HOST:-postgresql}/${POSTGRES_DB:-calendso}
# Needed to run migrations while using a connection pooler like PgBouncer
# Use the same one as DATABASE_URL if you are not using a connection pooler
- DATABASE_DIRECT_URL=postgresql://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@${DATABASE_HOST:-postgresql}/${POSTGRES_DB:-calendso}
# GOOGLE_API_CREDENTIALS={}
# Set this to 1 if you don't want Cal to collect anonymous usage
- CALCOM_TELEMETRY_DISABLED=1
# E-mail settings
# Configures the global From: header whilst sending emails.
- EMAIL_FROM=${EMAIL_FROM}
- EMAIL_FROM_NAME=${EMAIL_FROM_NAME}
# Configure SMTP settings (@see https://nodemailer.com/smtp/).
- EMAIL_SERVER_HOST=${EMAIL_SERVER_HOST}
- EMAIL_SERVER_PORT=${EMAIL_SERVER_PORT}
- EMAIL_SERVER_USER=${EMAIL_SERVER_USER}
- EMAIL_SERVER_PASSWORD=${EMAIL_SERVER_PASSWORD}
- NEXT_PUBLIC_APP_NAME="Cal.com"
# More info on ALLOWED_HOSTNAMES https://github.com/calcom/cal.com/issues/12201
- ALLOWED_HOSTNAMES=["${SERVICE_FQDN_CALCOM}"]
depends_on:
- postgresql
postgresql:
image: postgres:16-alpine
environment:
- POSTGRES_USER=${SERVICE_USER_POSTGRES}
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
- POSTGRES_DB=${POSTGRES_DATABASE:-calcom}
volumes:
- calcom-postgresql-data:/var/lib/postgresql/data
healthcheck:
test:
- CMD-SHELL
- pg_isready -U ${SERVICE_USER_POSTGRES} -d ${POSTGRES_DATABASE:-calcom}
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped