Merge branch 'next' into feature/authentik-provider

This commit is contained in:
🏔️ Peak
2024-12-11 15:24:26 +01:00
committed by GitHub
814 changed files with 31372 additions and 13434 deletions

View File

@@ -1,4 +1,10 @@
<?php
$version = include 'config/version.php';
echo $version;
// To prevent github actions from failing
function env()
{
return null;
}
$version = include 'config/constants.php';
echo $version['coolify']['version'] ?: 'unknown';

View File

@@ -21,14 +21,13 @@ function invalidTokenResponse()
function serializeApiResponse($data)
{
if ($data instanceof Collection) {
$data = $data->map(function ($d) {
return $data->map(function ($d) {
$d = collect($d)->sortKeys();
$created_at = data_get($d, 'created_at');
$updated_at = data_get($d, 'updated_at');
if ($created_at) {
unset($d['created_at']);
$d['created_at'] = $created_at;
}
if ($updated_at) {
unset($d['updated_at']);
@@ -50,8 +49,6 @@ function serializeApiResponse($data)
return $d;
});
return $data;
} else {
$d = collect($data)->sortKeys();
$created_at = data_get($d, 'created_at');
@@ -59,7 +56,6 @@ function serializeApiResponse($data)
if ($created_at) {
unset($d['created_at']);
$d['created_at'] = $created_at;
}
if ($updated_at) {
unset($d['updated_at']);

View File

@@ -44,13 +44,13 @@ function queue_application_deployment(Application $application, string $deployme
]);
if ($no_questions_asked) {
dispatch(new ApplicationDeploymentJob(
ApplicationDeploymentJob::dispatch(
application_deployment_queue_id: $deployment->id,
))->onQueue('high');
);
} elseif (next_queuable($server_id, $application_id)) {
dispatch(new ApplicationDeploymentJob(
ApplicationDeploymentJob::dispatch(
application_deployment_queue_id: $deployment->id,
))->onQueue('high');
);
}
}
function force_start_deployment(ApplicationDeploymentQueue $deployment)
@@ -59,9 +59,9 @@ function force_start_deployment(ApplicationDeploymentQueue $deployment)
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
]);
dispatch(new ApplicationDeploymentJob(
ApplicationDeploymentJob::dispatch(
application_deployment_queue_id: $deployment->id,
))->onQueue('high');
);
}
function queue_next_deployment(Application $application)
{
@@ -72,9 +72,9 @@ function queue_next_deployment(Application $application)
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
]);
dispatch(new ApplicationDeploymentJob(
ApplicationDeploymentJob::dispatch(
application_deployment_queue_id: $next_found->id,
))->onQueue('high');
);
}
}
@@ -91,7 +91,7 @@ function next_queuable(string $server_id, string $application_id): bool
$server = Server::find($server_id);
$concurrent_builds = $server->settings->concurrent_builds;
ray("serverId:{$server->id}", "concurrentBuilds:{$concurrent_builds}", "deployments:{$deployments->count()}", "sameApplicationDeployments:{$same_application_deployments->count()}")->green();
// ray("serverId:{$server->id}", "concurrentBuilds:{$concurrent_builds}", "deployments:{$deployments->count()}", "sameApplicationDeployments:{$same_application_deployments->count()}")->green();
if ($deployments->count() > $concurrent_builds) {
return false;
@@ -113,9 +113,9 @@ function next_after_cancel(?Server $server = null)
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
]);
dispatch(new ApplicationDeploymentJob(
ApplicationDeploymentJob::dispatch(
application_deployment_queue_id: $next->id,
))->onQueue('high');
);
}
break;
}

View File

@@ -46,7 +46,7 @@ const SPECIFIC_SERVICES = [
// Based on /etc/os-release
const SUPPORTED_OS = [
'ubuntu debian raspbian',
'ubuntu debian raspbian pop',
'centos fedora rhel ol rocky amzn almalinux',
'sles opensuse-leap opensuse-tumbleweed',
'arch',

View File

@@ -1,5 +1,6 @@
<?php
use App\Models\EnvironmentVariable;
use App\Models\Server;
use App\Models\StandaloneClickhouse;
use App\Models\StandaloneDocker;
@@ -48,7 +49,7 @@ function create_standalone_redis($environment_id, $destination_uuid, ?array $oth
}
$database = new StandaloneRedis;
$database->name = generate_database_name('redis');
$database->redis_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$redis_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$database->environment_id = $environment_id;
$database->destination_id = $destination->id;
$database->destination_type = $destination->getMorphClass();
@@ -57,6 +58,20 @@ function create_standalone_redis($environment_id, $destination_uuid, ?array $oth
}
$database->save();
EnvironmentVariable::create([
'key' => 'REDIS_PASSWORD',
'value' => $redis_password,
'standalone_redis_id' => $database->id,
'is_shared' => false,
]);
EnvironmentVariable::create([
'key' => 'REDIS_USERNAME',
'value' => 'default',
'standalone_redis_id' => $database->id,
'is_shared' => false,
]);
return $database;
}

View File

@@ -32,9 +32,8 @@ function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pul
return null;
});
$containers = $containers->filter();
return $containers;
return $containers->filter();
}
return $containers;
@@ -46,9 +45,8 @@ function getCurrentServiceContainerStatus(Server $server, int $id): Collection
if (! $server->isSwarm()) {
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.serviceId={$id}' --format '{{json .}}' "], $server);
$containers = format_docker_command_output_to_json($containers);
$containers = $containers->filter();
return $containers;
return $containers->filter();
}
return $containers;
@@ -67,7 +65,7 @@ function format_docker_command_output_to_json($rawOutput): Collection
return $outputLines
->reject(fn ($line) => empty($line))
->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR));
} catch (\Throwable $e) {
} catch (\Throwable) {
return collect([]);
}
}
@@ -104,14 +102,15 @@ function format_docker_envs_to_json($rawOutput)
return [$env[0] => $env[1]];
});
} catch (\Throwable $e) {
} catch (\Throwable) {
return collect([]);
}
}
function checkMinimumDockerEngineVersion($dockerVersion)
{
$majorDockerVersion = str($dockerVersion)->before('.')->value();
if ($majorDockerVersion <= 22) {
$requiredDockerVersion = str(config('constants.docker.minimum_required_version'))->before('.')->value();
if ($majorDockerVersion < $requiredDockerVersion) {
$dockerVersion = null;
}
@@ -193,7 +192,7 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
{
$labels = collect([]);
$labels->push('coolify.managed=true');
$labels->push('coolify.version='.config('version'));
$labels->push('coolify.version='.config('constants.coolify.version'));
$labels->push('coolify.'.$type.'Id='.$id);
$labels->push("coolify.type=$type");
$labels->push('coolify.name='.$name);
@@ -207,12 +206,12 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
}
function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
{
if ($resource->getMorphClass() === 'App\Models\ServiceApplication') {
if ($resource->getMorphClass() === \App\Models\ServiceApplication::class) {
$uuid = data_get($resource, 'uuid');
$server = data_get($resource, 'service.server');
$environment_variables = data_get($resource, 'service.environment_variables');
$type = $resource->serviceType();
} elseif ($resource->getMorphClass() === 'App\Models\Application') {
} elseif ($resource->getMorphClass() === \App\Models\Application::class) {
$uuid = data_get($resource, 'uuid');
$server = data_get($resource, 'destination.server');
$environment_variables = data_get($resource, 'environment_variables');
@@ -227,16 +226,18 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
case $type?->contains('minio'):
$MINIO_BROWSER_REDIRECT_URL = $variables->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first();
$MINIO_SERVER_URL = $variables->where('key', 'MINIO_SERVER_URL')->first();
if (is_null($MINIO_BROWSER_REDIRECT_URL) || is_null($MINIO_SERVER_URL)) {
return $payload;
return collect([]);
}
if (is_null($MINIO_BROWSER_REDIRECT_URL?->value)) {
$MINIO_BROWSER_REDIRECT_URL?->update([
if (str($MINIO_BROWSER_REDIRECT_URL->value ?? '')->isEmpty()) {
$MINIO_BROWSER_REDIRECT_URL->update([
'value' => generateFqdn($server, 'console-'.$uuid, true),
]);
}
if (is_null($MINIO_SERVER_URL?->value)) {
$MINIO_SERVER_URL?->update([
if (str($MINIO_SERVER_URL->value ?? '')->isEmpty()) {
$MINIO_SERVER_URL->update([
'value' => generateFqdn($server, 'minio-'.$uuid, true),
]);
}
@@ -248,16 +249,18 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
case $type?->contains('logto'):
$LOGTO_ENDPOINT = $variables->where('key', 'LOGTO_ENDPOINT')->first();
$LOGTO_ADMIN_ENDPOINT = $variables->where('key', 'LOGTO_ADMIN_ENDPOINT')->first();
if (is_null($LOGTO_ENDPOINT) || is_null($LOGTO_ADMIN_ENDPOINT)) {
return $payload;
return collect([]);
}
if (is_null($LOGTO_ENDPOINT?->value)) {
$LOGTO_ENDPOINT?->update([
if (str($LOGTO_ENDPOINT->value ?? '')->isEmpty()) {
$LOGTO_ENDPOINT->update([
'value' => generateFqdn($server, 'logto-'.$uuid),
]);
}
if (is_null($LOGTO_ADMIN_ENDPOINT?->value)) {
$LOGTO_ADMIN_ENDPOINT?->update([
if (str($LOGTO_ADMIN_ENDPOINT->value ?? '')->isEmpty()) {
$LOGTO_ADMIN_ENDPOINT->update([
'value' => generateFqdn($server, 'logto-admin-'.$uuid),
]);
}
@@ -279,13 +282,16 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
$labels->push("caddy_ingress_network={$network}");
}
foreach ($domains as $loop => $domain) {
$loop = $loop;
$url = Url::fromString($domain);
$host = $url->getHost();
$path = $url->getPath();
$host_without_www = str($host)->replace('www.', '');
$schema = $url->getScheme();
$port = $url->getPort();
$handle = 'handle_path';
if (! $is_stripprefix_enabled) {
$handle = 'handle';
}
if (is_null($port) && ! is_null($onlyPort)) {
$port = $onlyPort;
}
@@ -297,11 +303,11 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
$labels->push("caddy_{$loop}.try_files={path} /index.html /index.php");
if ($port) {
$labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams $port}}");
$labels->push("caddy_{$loop}.{$handle}.{$loop}_reverse_proxy={{upstreams $port}}");
} else {
$labels->push("caddy_{$loop}.handle_path.{$loop}_reverse_proxy={{upstreams}}");
$labels->push("caddy_{$loop}.{$handle}.{$loop}_reverse_proxy={{upstreams}}");
}
$labels->push("caddy_{$loop}.handle_path={$path}*");
$labels->push("caddy_{$loop}.{$handle}={$path}*");
if ($is_gzip_enabled) {
$labels->push("caddy_{$loop}.encode=zstd gzip");
}
@@ -335,10 +341,11 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if (preg_match('/coolify\.traefik\.middlewares=(.*)/', $item, $matches)) {
return explode(',', $matches[1]);
}
return null;
})->flatten()
->filter()
->unique();
->filter()
->unique();
}
foreach ($domains as $loop => $domain) {
try {
@@ -361,8 +368,11 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$https_label = "https-{$loop}-{$uuid}-{$service_name}";
}
if (str($image)->contains('ghost')) {
$labels->push("traefik.http.middlewares.redir-ghost.redirectregex.regex=^{$path}/(.*)");
$labels->push('traefik.http.middlewares.redir-ghost.redirectregex.replacement=/$1');
$labels->push("traefik.http.middlewares.redir-ghost-{$uuid}.redirectregex.regex=^{$path}/(.*)");
$labels->push("traefik.http.middlewares.redir-ghost-{$uuid}.redirectregex.replacement=/$1");
$labels->push("caddy_{$loop}.handle_path.{$loop}_redir-ghost-{$uuid}.handler=rewrite");
$labels->push("caddy_{$loop}.handle_path.{$loop}_redir-ghost-{$uuid}.rewrite.regexp=^{$path}/(.*)");
$labels->push("caddy_{$loop}.handle_path.{$loop}_redir-ghost-{$uuid}.rewrite.replacement=/$1");
}
$to_www_name = "{$loop}-{$uuid}-to-www";
@@ -388,7 +398,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if ($path !== '/') {
// Middleware handling
$middlewares = collect([]);
if ($is_stripprefix_enabled && !str($image)->contains('ghost')) {
if ($is_stripprefix_enabled && ! str($image)->contains('ghost')) {
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
$middlewares->push("{$https_label}-stripprefix");
}
@@ -396,13 +406,13 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$middlewares->push('gzip');
}
if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost');
$middlewares->push("redir-ghost-{$uuid}");
}
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www);
$middlewares->push($to_non_www_name);
}
if ($redirect_direction === 'www' && !str($host)->startsWith('www.')) {
if ($redirect_direction === 'www' && ! str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_www);
$middlewares->push($to_www_name);
}
@@ -417,9 +427,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$middlewares = collect([]);
if ($is_gzip_enabled) {
$middlewares->push('gzip');
}
}
if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost');
$middlewares->push("redir-ghost-{$uuid}");
}
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www);
@@ -468,7 +478,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$middlewares->push('gzip');
}
if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost');
$middlewares->push("redir-ghost-{$uuid}");
}
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www);
@@ -491,7 +501,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$middlewares->push('gzip');
}
if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost');
$middlewares->push("redir-ghost-{$uuid}");
}
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www);
@@ -510,7 +520,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
}
}
}
} catch (\Throwable $e) {
} catch (\Throwable) {
continue;
}
}
@@ -581,7 +591,6 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
redirect_direction: $application->redirect
));
}
}
} else {
if (data_get($preview, 'fqdn')) {
@@ -633,7 +642,6 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
}
}
return $labels->all();
@@ -658,7 +666,7 @@ function isDatabaseImage(?string $image = null)
return false;
}
function convert_docker_run_to_compose(?string $custom_docker_run_options = null)
function convertDockerRunToCompose(?string $custom_docker_run_options = null)
{
$options = [];
$compose_options = collect([]);
@@ -683,9 +691,17 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
'--privileged' => 'privileged',
'--ip' => 'ip',
'--shm-size' => 'shm_size',
'--gpus' => 'gpus',
]);
foreach ($matches as $match) {
$option = $match[1];
if ($option === '--gpus') {
$regexForParsingDeviceIds = '/device=([0-9A-Za-z-,]+)/';
preg_match($regexForParsingDeviceIds, $custom_docker_run_options, $device_matches);
$value = $device_matches[1] ?? 'all';
$options[$option][] = $value;
$options[$option] = array_unique($options[$option]);
}
if (isset($match[2]) && $match[2] !== '') {
$value = $match[2];
$options[$option][] = $value;
@@ -698,7 +714,6 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
$options = collect($options);
// Easily get mappings from https://github.com/composerize/composerize/blob/master/packages/composerize/src/mappings.js
foreach ($options as $option => $value) {
// ray($option,$value);
if (! data_get($mapping, $option)) {
continue;
}
@@ -727,6 +742,28 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
if (! is_null($value) && is_array($value) && count($value) > 0) {
$compose_options->put($mapping[$option], $value[0]);
}
} elseif ($option === '--gpus') {
$payload = [
'driver' => 'nvidia',
'capabilities' => ['gpu'],
];
if (! is_null($value) && is_array($value) && count($value) > 0) {
if (str($value[0]) != 'all') {
if (str($value[0])->contains(',')) {
$payload['device_ids'] = str($value[0])->explode(',')->toArray();
} else {
$payload['device_ids'] = [$value[0]];
}
}
}
ray($payload);
$compose_options->put('deploy', [
'resources' => [
'reservations' => [
'devices' => [$payload],
],
],
]);
} else {
if ($list_options->contains($option)) {
if ($compose_options->has($mapping[$option])) {
@@ -748,7 +785,7 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
return $compose_options->toArray();
}
function generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $network)
function generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $network)
{
$ipv4 = data_get($docker_run_options, 'ip.0');
$ipv6 = data_get($docker_run_options, 'ip6.0');

View File

@@ -3,6 +3,7 @@
use App\Models\GithubApp;
use App\Models\GitlabApp;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Lcobucci\JWT\Encoding\ChainedFormatter;
@@ -16,7 +17,7 @@ function generate_github_installation_token(GithubApp $source)
$signingKey = InMemory::plainText($source->privateKey->private_key);
$algorithm = new Sha256;
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
$now = new DateTimeImmutable;
$now = CarbonImmutable::now();
$now = $now->setTime($now->format('H'), $now->format('i'));
$issuedToken = $tokenBuilder
->issuedBy($source->app_id)
@@ -40,16 +41,15 @@ function generate_github_jwt_token(GithubApp $source)
$signingKey = InMemory::plainText($source->privateKey->private_key);
$algorithm = new Sha256;
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
$now = new DateTimeImmutable;
$now = CarbonImmutable::now();
$now = $now->setTime($now->format('H'), $now->format('i'));
$issuedToken = $tokenBuilder
return $tokenBuilder
->issuedBy($source->app_id)
->issuedAt($now->modify('-1 minute'))
->expiresAt($now->modify('+10 minutes'))
->getToken($algorithm, $signingKey)
->toString();
return $issuedToken;
}
function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $method = 'get', ?array $data = null, bool $throwError = true)
@@ -57,7 +57,7 @@ function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $m
if (is_null($source)) {
throw new \Exception('Not implemented yet.');
}
if ($source->getMorphClass() == 'App\Models\GithubApp') {
if ($source->getMorphClass() === \App\Models\GithubApp::class) {
if ($source->is_public) {
$response = Http::github($source->api_url)->$method($endpoint);
} else {

View File

@@ -0,0 +1,87 @@
<?php
use App\Models\InstanceSettings;
use App\Models\Team;
use App\Notifications\Internal\GeneralNotification;
use Illuminate\Mail\Message;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Mail;
function is_transactional_emails_enabled(): bool
{
$settings = instanceSettings();
return $settings->smtp_enabled || $settings->resend_enabled;
}
function send_internal_notification(string $message): void
{
try {
$team = Team::find(0);
$team?->notify(new GeneralNotification($message));
} catch (\Throwable $e) {
ray($e->getMessage());
}
}
function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null): void
{
$settings = instanceSettings();
$type = set_transanctional_email_settings($settings);
if (! $type) {
throw new Exception('No email settings found.');
}
if ($cc) {
Mail::send(
[],
[],
fn (Message $message) => $message
->to($email)
->replyTo($email)
->cc($cc)
->subject($mail->subject)
->html((string) $mail->render())
);
} else {
Mail::send(
[],
[],
fn (Message $message) => $message
->to($email)
->subject($mail->subject)
->html((string) $mail->render())
);
}
}
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string //
{
if (! $settings) {
$settings = instanceSettings();
}
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
if (data_get($settings, 'resend_enabled')) {
config()->set('mail.default', 'resend');
config()->set('resend.api_key', data_get($settings, 'resend_api_key'));
return 'resend';
}
if (data_get($settings, 'smtp_enabled')) {
config()->set('mail.default', 'smtp');
config()->set('mail.mailers.smtp', [
'transport' => 'smtp',
'host' => data_get($settings, 'smtp_host'),
'port' => data_get($settings, 'smtp_port'),
'encryption' => data_get($settings, 'smtp_encryption'),
'username' => data_get($settings, 'smtp_username'),
'password' => data_get($settings, 'smtp_password'),
'timeout' => data_get($settings, 'smtp_timeout'),
'local_domain' => null,
]);
return 'smtp';
}
return null;
}

View File

@@ -16,12 +16,10 @@ function collectProxyDockerNetworksByServer(Server $server)
return collect();
}
$networks = instant_remote_process(['docker inspect --format="{{json .NetworkSettings.Networks }}" coolify-proxy'], $server, false);
$networks = collect($networks)->map(function ($network) {
return collect($networks)->map(function ($network) {
return collect(json_decode($network))->keys();
})->flatten()->unique();
return $networks;
}
function collectDockerNetworksByServer(Server $server)
{
@@ -175,13 +173,12 @@ function generate_default_proxy_configuration(Server $server)
],
'volumes' => [
'/var/run/docker.sock:/var/run/docker.sock:ro',
"{$proxy_path}:/traefik",
],
'command' => [
'--ping=true',
'--ping.entrypoint=http',
'--api.dashboard=true',
'--api.insecure=false',
'--entrypoints.http.address=:80',
'--entrypoints.https.address=:443',
'--entrypoints.http.http.encodequerysemicolons=true',
@@ -189,21 +186,26 @@ function generate_default_proxy_configuration(Server $server)
'--entrypoints.https.http.encodequerysemicolons=true',
'--entryPoints.https.http2.maxConcurrentStreams=50',
'--entrypoints.https.http3',
'--providers.docker.exposedbydefault=false',
'--providers.file.directory=/traefik/dynamic/',
'--providers.docker.exposedbydefault=false',
'--providers.file.watch=true',
'--certificatesresolvers.letsencrypt.acme.httpchallenge=true',
'--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json',
'--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http',
'--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json',
],
'labels' => $labels,
],
],
];
if (isDev()) {
// $config['services']['traefik']['command'][] = "--log.level=debug";
$config['services']['traefik']['command'][] = '--api.insecure=true';
$config['services']['traefik']['command'][] = '--log.level=debug';
$config['services']['traefik']['command'][] = '--accesslog.filepath=/traefik/access.log';
$config['services']['traefik']['command'][] = '--accesslog.bufferingsize=100';
$config['services']['traefik']['volumes'][] = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/:/traefik';
} else {
$config['services']['traefik']['command'][] = '--api.insecure=false';
$config['services']['traefik']['volumes'][] = "{$proxy_path}:/traefik";
}
if ($server->isSwarm()) {
data_forget($config, 'services.traefik.container_name');
@@ -241,9 +243,11 @@ function generate_default_proxy_configuration(Server $server)
'ports' => [
'80:80',
'443:443',
'443:443/udp',
],
'labels' => [
'coolify.managed=true',
'coolify.proxy=true',
],
'volumes' => [
'/var/run/docker.sock:/var/run/docker.sock:ro',

View File

@@ -124,7 +124,7 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
associative: true,
flags: JSON_THROW_ON_ERROR
);
} catch (\JsonException $exception) {
} catch (\JsonException) {
return collect([]);
}
$seenCommands = collect();
@@ -204,7 +204,7 @@ function checkRequiredCommands(Server $server)
}
try {
instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'apt update && apt install -y {$command}'"], $server);
} catch (\Throwable $e) {
} catch (\Throwable) {
break;
}
$commandFound = instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'command -v {$command}'"], $server, false);

View File

@@ -24,7 +24,7 @@ function replaceVariables(string $variable): Stringable
function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Application $oneService, bool $isInit = false)
{
try {
if ($oneService->getMorphClass() === 'App\Models\Application') {
if ($oneService->getMorphClass() === \App\Models\Application::class) {
$workdir = $oneService->workdir();
$server = $oneService->destination->server;
} else {
@@ -51,7 +51,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
// Exists and is a directory
$isDir = instant_remote_process(["test -d $fileLocation && echo OK || echo NOK"], $server);
if ($isFile == 'OK') {
if ($isFile === 'OK') {
// If its a file & exists
$filesystemContent = instant_remote_process(["cat $fileLocation"], $server);
if ($fileVolume->is_based_on_git) {
@@ -59,12 +59,12 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
}
$fileVolume->is_directory = false;
$fileVolume->save();
} elseif ($isDir == 'OK') {
} elseif ($isDir === 'OK') {
// If its a directory & exists
$fileVolume->content = null;
$fileVolume->is_directory = true;
$fileVolume->save();
} elseif ($isFile == 'NOK' && $isDir == 'NOK' && ! $fileVolume->is_directory && $isInit && $content) {
} elseif ($isFile === 'NOK' && $isDir === 'NOK' && ! $fileVolume->is_directory && $isInit && $content) {
// Does not exists (no dir or file), not flagged as directory, is init, has content
$fileVolume->content = $content;
$fileVolume->is_directory = false;
@@ -75,13 +75,13 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
"mkdir -p $dir",
"echo '$content' | base64 -d | tee $fileLocation",
], $server);
} elseif ($isFile == 'NOK' && $isDir == 'NOK' && $fileVolume->is_directory && $isInit) {
} elseif ($isFile === 'NOK' && $isDir === 'NOK' && $fileVolume->is_directory && $isInit) {
// Does not exists (no dir or file), flagged as directory, is init
$fileVolume->content = null;
$fileVolume->is_directory = true;
$fileVolume->save();
instant_remote_process(["mkdir -p $fileLocation"], $server);
} elseif ($isFile == 'NOK' && $isDir == 'NOK' && ! $fileVolume->is_directory && $isInit && is_null($content)) {
} elseif ($isFile === 'NOK' && $isDir === 'NOK' && ! $fileVolume->is_directory && $isInit && is_null($content)) {
// Does not exists (no dir or file), not flagged as directory, is init, has no content => create directory
$fileVolume->content = null;
$fileVolume->is_directory = true;
@@ -245,8 +245,5 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
}
function serviceKeys()
{
$services = get_service_templates();
$serviceKeys = $services->keys();
return $serviceKeys;
return get_service_templates()->keys();
}

View File

@@ -7,6 +7,7 @@ use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use App\Models\ApplicationPreview;
use App\Models\EnvironmentVariable;
use App\Models\GithubApp;
use App\Models\InstanceSettings;
use App\Models\LocalFileVolume;
use App\Models\LocalPersistentVolume;
@@ -24,21 +25,17 @@ use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use App\Models\Team;
use App\Models\User;
use App\Notifications\Channels\DiscordChannel;
use App\Notifications\Channels\EmailChannel;
use App\Notifications\Channels\TelegramChannel;
use App\Notifications\Internal\GeneralNotification;
use Carbon\CarbonImmutable;
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Mail\Message;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Process\Pool;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Validator;
@@ -86,8 +83,31 @@ function metrics_dir(): string
return base_configuration_dir().'/metrics';
}
function sanitize_string(?string $input = null): ?string
{
if (is_null($input)) {
return null;
}
// Remove any HTML/PHP tags
$sanitized = strip_tags($input);
// Convert special characters to HTML entities
$sanitized = htmlspecialchars($sanitized, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// Remove any control characters
$sanitized = preg_replace('/[\x00-\x1F\x7F]/u', '', $sanitized);
// Trim whitespace
$sanitized = trim($sanitized);
return $sanitized;
}
function generate_readme_file(string $name, string $updated_at): string
{
$name = sanitize_string($name);
$updated_at = sanitize_string($updated_at);
return "Resource name: $name\nLatest Deployment Date: $updated_at";
}
@@ -98,12 +118,12 @@ function isInstanceAdmin()
function currentTeam()
{
return auth()?->user()?->currentTeam() ?? null;
return Auth::user()?->currentTeam() ?? null;
}
function showBoarding(): bool
{
if (auth()->user()?->isMember()) {
if (Auth::user()?->isMember()) {
return false;
}
@@ -112,21 +132,20 @@ function showBoarding(): bool
function refreshSession(?Team $team = null): void
{
if (! $team) {
if (auth()->user()?->currentTeam()) {
$team = Team::find(auth()->user()->currentTeam()->id);
if (Auth::user()->currentTeam()) {
$team = Team::find(Auth::user()->currentTeam()->id);
} else {
$team = User::find(auth()->user()->id)->teams->first();
$team = User::find(Auth::id())->teams->first();
}
}
Cache::forget('team:'.auth()->user()->id);
Cache::remember('team:'.auth()->user()->id, 3600, function () use ($team) {
Cache::forget('team:'.Auth::id());
Cache::remember('team:'.Auth::id(), 3600, function () use ($team) {
return $team;
});
session(['currentTeam' => $team]);
}
function handleError(?Throwable $error = null, ?Livewire\Component $livewire = null, ?string $customErrorMessage = null)
{
ray($error);
if ($error instanceof TooManyRequestsException) {
if (isset($livewire)) {
return $livewire->dispatch('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.");
@@ -142,6 +161,10 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n
return 'Duplicate entry found. Please use a different name.';
}
if ($error instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
abort(404);
}
if ($error instanceof Throwable) {
$message = $error->getMessage();
} else {
@@ -164,14 +187,11 @@ function get_route_parameters(): array
function get_latest_sentinel_version(): string
{
try {
$response = Http::get('https://cdn.coollabs.io/sentinel/versions.json');
$response = Http::get('https://cdn.coollabs.io/coolify/versions.json');
$versions = $response->json();
return data_get($versions, 'sentinel.version');
} catch (\Throwable $e) {
//throw $e;
ray($e->getMessage());
return data_get($versions, 'coolify.sentinel.version');
} catch (\Throwable) {
return '0.0.0';
}
}
@@ -239,43 +259,6 @@ function generate_application_name(string $git_repository, string $git_branch, ?
return Str::kebab("$git_repository:$git_branch-$cuid");
}
function is_transactional_emails_active(): bool
{
return isEmailEnabled(\App\Models\InstanceSettings::get());
}
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string
{
if (! $settings) {
$settings = instanceSettings();
}
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
if (data_get($settings, 'resend_enabled')) {
config()->set('mail.default', 'resend');
config()->set('resend.api_key', data_get($settings, 'resend_api_key'));
return 'resend';
}
if (data_get($settings, 'smtp_enabled')) {
config()->set('mail.default', 'smtp');
config()->set('mail.mailers.smtp', [
'transport' => 'smtp',
'host' => data_get($settings, 'smtp_host'),
'port' => data_get($settings, 'smtp_port'),
'encryption' => data_get($settings, 'smtp_encryption'),
'username' => data_get($settings, 'smtp_username'),
'password' => data_get($settings, 'smtp_password'),
'timeout' => data_get($settings, 'smtp_timeout'),
'local_domain' => null,
]);
return 'smtp';
}
return null;
}
function base_ip(): string
{
if (isDev()) {
@@ -300,7 +283,7 @@ function getFqdnWithoutPort(string $fqdn)
$path = $url->getPath();
return "$scheme://$host$path";
} catch (\Throwable $e) {
} catch (\Throwable) {
return $fqdn;
}
}
@@ -355,7 +338,7 @@ function isDev(): bool
function isCloud(): bool
{
return ! config('coolify.self_hosted');
return ! config('constants.coolify.self_hosted');
}
function translate_cron_expression($expression_to_validate): string
@@ -368,6 +351,9 @@ function translate_cron_expression($expression_to_validate): string
}
function validate_cron_expression($expression_to_validate): bool
{
if (empty($expression_to_validate)) {
return false;
}
$isValid = false;
$expression = new CronExpression($expression_to_validate);
$isValid = $expression->isValid();
@@ -378,80 +364,12 @@ function validate_cron_expression($expression_to_validate): bool
return $isValid;
}
function send_internal_notification(string $message): void
{
try {
$team = Team::find(0);
$team?->notify(new GeneralNotification($message));
} catch (\Throwable $e) {
ray($e->getMessage());
}
}
function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null): void
{
$settings = instanceSettings();
$type = set_transanctional_email_settings($settings);
if (! $type) {
throw new Exception('No email settings found.');
}
if ($cc) {
Mail::send(
[],
[],
fn (Message $message) => $message
->to($email)
->replyTo($email)
->cc($cc)
->subject($mail->subject)
->html((string) $mail->render())
);
} else {
Mail::send(
[],
[],
fn (Message $message) => $message
->to($email)
->subject($mail->subject)
->html((string) $mail->render())
);
}
}
function isTestEmailEnabled($notifiable)
{
if (data_get($notifiable, 'use_instance_email_settings') && isInstanceAdmin()) {
return true;
} elseif (data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') && auth()->user()->isAdminFromSession()) {
return true;
}
return false;
}
function isEmailEnabled($notifiable)
function validate_timezone(string $timezone): bool
{
return data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') || data_get($notifiable, 'use_instance_email_settings');
return in_array($timezone, timezone_identifiers_list());
}
function setNotificationChannels($notifiable, $event)
{
$channels = [];
$isEmailEnabled = isEmailEnabled($notifiable);
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
$isSubscribedToEmailEvent = data_get($notifiable, "smtp_notifications_$event");
$isSubscribedToDiscordEvent = data_get($notifiable, "discord_notifications_$event");
$isSubscribedToTelegramEvent = data_get($notifiable, "telegram_notifications_$event");
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
$channels[] = DiscordChannel::class;
}
if ($isEmailEnabled && $isSubscribedToEmailEvent) {
$channels[] = EmailChannel::class;
}
if ($isTelegramEnabled && $isSubscribedToTelegramEvent) {
$channels[] = TelegramChannel::class;
}
return $channels;
}
function parseEnvFormatToArray($env_file_contents)
{
$env_array = [];
@@ -496,9 +414,8 @@ function generateFqdn(Server $server, string $random, bool $forceHttps = false):
if ($forceHttps) {
$scheme = 'https';
}
$finalFqdn = "$scheme://{$random}.$host$path";
return $finalFqdn;
return "$scheme://{$random}.$host$path";
}
function sslip(Server $server)
{
@@ -536,7 +453,7 @@ function get_service_templates(bool $force = false): Collection
$services = $response->json();
return collect($services);
} catch (\Throwable $e) {
} catch (\Throwable) {
$services = File::get(base_path('templates/service-templates.json'));
return collect(json_decode($services))->sortKeys();
@@ -643,14 +560,13 @@ function queryResourcesByUuid(string $uuid)
return $resource;
}
function generatTagDeployWebhook($tag_name)
function generateTagDeployWebhook($tag_name)
{
$baseUrl = base_url();
$api = Url::fromString($baseUrl).'/api/v1';
$endpoint = "/deploy?tag=$tag_name";
$url = $api.$endpoint;
return $url;
return $api.$endpoint;
}
function generateDeployWebhook($resource)
{
@@ -658,20 +574,18 @@ function generateDeployWebhook($resource)
$api = Url::fromString($baseUrl).'/api/v1';
$endpoint = '/deploy';
$uuid = data_get($resource, 'uuid');
$url = $api.$endpoint."?uuid=$uuid&force=false";
return $url;
return $api.$endpoint."?uuid=$uuid&force=false";
}
function generateGitManualWebhook($resource, $type)
{
if ($resource->source_id !== 0 && ! is_null($resource->source_id)) {
return null;
}
if ($resource->getMorphClass() === 'App\Models\Application') {
if ($resource->getMorphClass() === \App\Models\Application::class) {
$baseUrl = base_url();
$api = Url::fromString($baseUrl)."/webhooks/source/$type/events/manual";
return $api;
return Url::fromString($baseUrl)."/webhooks/source/$type/events/manual";
}
return null;
@@ -683,7 +597,7 @@ function removeAnsiColors($text)
function getTopLevelNetworks(Service|Application $resource)
{
if ($resource->getMorphClass() === 'App\Models\Service') {
if ($resource->getMorphClass() === \App\Models\Service::class) {
if ($resource->docker_compose_raw) {
try {
$yaml = Yaml::parse($resource->docker_compose_raw);
@@ -738,7 +652,7 @@ function getTopLevelNetworks(Service|Application $resource)
return $topLevelNetworks->keys();
}
} elseif ($resource->getMorphClass() === 'App\Models\Application') {
} elseif ($resource->getMorphClass() === \App\Models\Application::class) {
try {
$yaml = Yaml::parse($resource->docker_compose_raw);
} catch (\Exception $e) {
@@ -932,6 +846,15 @@ function generateEnvValue(string $command, Service|Application|null $service = n
case 'REALBASE64_32':
$generatedValue = base64_encode(Str::random(32));
break;
case 'HEX_32':
$generatedValue = bin2hex(Str::random(32));
break;
case 'HEX_64':
$generatedValue = bin2hex(Str::random(64));
break;
case 'HEX_128':
$generatedValue = bin2hex(Str::random(128));
break;
case 'USER':
$generatedValue = Str::random(16);
break;
@@ -945,7 +868,7 @@ function generateEnvValue(string $command, Service|Application|null $service = n
$key = InMemory::plainText($signingKey);
$algorithm = new Sha256;
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
$now = new DateTimeImmutable;
$now = CarbonImmutable::now();
$now = $now->setTime($now->format('H'), $now->format('i'));
$token = $tokenBuilder
->issuedBy('supabase')
@@ -965,7 +888,7 @@ function generateEnvValue(string $command, Service|Application|null $service = n
$key = InMemory::plainText($signingKey);
$algorithm = new Sha256;
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
$now = new DateTimeImmutable;
$now = CarbonImmutable::now();
$now = $now->setTime($now->format('H'), $now->format('i'));
$token = $tokenBuilder
->issuedBy('supabase')
@@ -986,7 +909,7 @@ function generateEnvValue(string $command, Service|Application|null $service = n
function getRealtime()
{
$envDefined = env('PUSHER_PORT');
$envDefined = config('constants.pusher.port');
if (empty($envDefined)) {
$url = Url::fromString(Request::getSchemeAndHttpHost());
$port = $url->getPort();
@@ -1048,7 +971,7 @@ function validate_dns_entry(string $fqdn, Server $server)
}
}
}
} catch (\Exception $e) {
} catch (\Exception) {
}
}
ray("Found match: $found_matching_ip");
@@ -1145,7 +1068,7 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId =
function check_domain_usage(ServiceApplication|Application|null $resource = null, ?string $domain = null)
{
if ($resource) {
if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose') {
if ($resource->getMorphClass() === \App\Models\Application::class && $resource->build_pack === 'dockercompose') {
$domains = data_get(json_decode($resource->docker_compose_domains, true), '*.domain');
$domains = collect($domains);
} else {
@@ -1338,13 +1261,6 @@ function isAnyDeploymentInprogress()
exit(0);
}
function generateSentinelToken()
{
$token = Str::random(64);
return $token;
}
function isBase64Encoded($strValue)
{
return base64_encode(base64_decode($strValue, true)) === $strValue;
@@ -1416,7 +1332,7 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
return $volume;
}
if (get_class($resource) === "App\Models\Application") {
if (get_class($resource) === \App\Models\Application::class) {
$dir = base_configuration_dir().'/applications/'.$resource->uuid;
} else {
$dir = base_configuration_dir().'/services/'.$resource->service->uuid;
@@ -1456,7 +1372,7 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
}
}
$slugWithoutUuid = Str::slug($source, '-');
if (get_class($resource) === "App\Models\Application") {
if (get_class($resource) === \App\Models\Application::class) {
$name = "{$resource->uuid}_{$slugWithoutUuid}";
} else {
$name = "{$resource->service->uuid}_{$slugWithoutUuid}";
@@ -1499,7 +1415,7 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
function parseDockerComposeFile(Service|Application $resource, bool $isNew = false, int $pull_request_id = 0, ?int $preview_id = null)
{
if ($resource->getMorphClass() === 'App\Models\Service') {
if ($resource->getMorphClass() === \App\Models\Service::class) {
if ($resource->docker_compose_raw) {
try {
$yaml = Yaml::parse($resource->docker_compose_raw);
@@ -2213,10 +2129,10 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} else {
return collect([]);
}
} elseif ($resource->getMorphClass() === 'App\Models\Application') {
} elseif ($resource->getMorphClass() === \App\Models\Application::class) {
try {
$yaml = Yaml::parse($resource->docker_compose_raw);
} catch (\Exception $e) {
} catch (\Exception) {
return;
}
$server = $resource->destination->server;
@@ -2962,7 +2878,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
try {
$yaml = Yaml::parse($compose);
} catch (\Exception $e) {
} catch (\Exception) {
return collect([]);
}
@@ -3099,7 +3015,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
}
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
if ($value && get_class($value) === \Illuminate\Support\Stringable::class && $value->startsWith('/')) {
$path = $value->value();
if ($path !== '/') {
$fqdn = "$fqdn$path";
@@ -3190,7 +3106,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
'is_build_time' => false,
'is_preview' => false,
]);
} else {
$value = generateEnvValue($command, $resource);
$resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
@@ -3618,7 +3533,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
'is_required' => $isRequired,
]);
}
}
}
if ($isApplication) {
@@ -3792,7 +3706,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
service_name: $serviceName,
image: $image,
predefinedPort: $predefinedPort
));
}
}
@@ -3983,20 +3896,19 @@ function convertComposeEnvironmentToArray($environment)
}
return $convertedServiceVariables;
}
function instanceSettings()
{
return InstanceSettings::get();
}
function loadConfigFromGit(string $repository, string $branch, string $base_directory, int $server_id, int $team_id) {
function loadConfigFromGit(string $repository, string $branch, string $base_directory, int $server_id, int $team_id)
{
$server = Server::find($server_id)->where('team_id', $team_id)->first();
if (!$server) {
if (! $server) {
return;
}
$uuid = new Cuid2();
$uuid = new Cuid2;
$cloneCommand = "git clone --no-checkout -b $branch $repository .";
$workdir = rtrim($base_directory, '/');
$fileList = collect([".$workdir/coolify.json"]);
@@ -4013,7 +3925,141 @@ function loadConfigFromGit(string $repository, string $branch, string $base_dire
]);
try {
return instant_remote_process($commands, $server);
} catch (\Exception $e) {
// continue
} catch (\Exception) {
// continue
}
}
function loggy($message = null, array $context = [])
{
if (! isDev()) {
return;
}
if (function_exists('ray') && config('app.debug')) {
ray($message, $context);
}
if (is_null($message)) {
return app('log');
}
return app('log')->debug($message, $context);
}
function sslipDomainWarning(string $domains)
{
$domains = str($domains)->trim()->explode(',');
$showSslipHttpsWarning = false;
$domains->each(function ($domain) use (&$showSslipHttpsWarning) {
if (str($domain)->contains('https') && str($domain)->contains('sslip')) {
$showSslipHttpsWarning = true;
}
});
return $showSslipHttpsWarning;
}
function isEmailRateLimited(string $limiterKey, int $decaySeconds = 3600, ?callable $callbackOnSuccess = null): bool
{
if (isDev()) {
$decaySeconds = 120;
}
$rateLimited = false;
$executed = RateLimiter::attempt(
$limiterKey,
$maxAttempts = 0,
function () use (&$rateLimited, &$limiterKey, $callbackOnSuccess) {
isDev() && loggy('Rate limit not reached for '.$limiterKey);
$rateLimited = false;
if ($callbackOnSuccess) {
$callbackOnSuccess();
}
},
$decaySeconds,
);
if (! $executed) {
isDev() && loggy('Rate limit reached for '.$limiterKey.'. Rate limiter will be disabled for '.$decaySeconds.' seconds.');
$rateLimited = true;
}
return $rateLimited;
}
function defaultNginxConfiguration(): string
{
return 'server {
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri.html $uri/index.html $uri/index.htm $uri/ /index.html /index.htm =404;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
try_files $uri @redirect_to_index;
internal;
}
error_page 404 = @handle_404;
location @handle_404 {
root /usr/share/nginx/html;
try_files /404.html @redirect_to_index;
internal;
}
location @redirect_to_index {
return 302 /;
}
}';
}
function convertGitUrl(string $gitRepository, string $deploymentType, ?GithubApp $source = null): array
{
$repository = $gitRepository;
$providerInfo = [
'host' => null,
'user' => 'git',
'port' => 22,
'repository' => $gitRepository,
];
$sshMatches = [];
$matches = [];
// Let's try and parse the string to detect if it's a valid SSH string or not
preg_match('/((.*?)\:\/\/)?(.*@.*:.*)/', $gitRepository, $sshMatches);
if ($deploymentType === 'deploy_key' && empty($sshMatches) && $source) {
// If this happens, the user may have provided an HTTP URL when they needed an SSH one
// Let's try and fix that for known Git providers
switch ($source->getMorphClass()) {
case \App\Models\GithubApp::class:
$providerInfo['host'] = Url::fromString($source->html_url)->getHost();
$providerInfo['port'] = $source->custom_port;
$providerInfo['user'] = $source->custom_user;
break;
}
if (! empty($providerInfo['host'])) {
// Until we do not support more providers with App (like GithubApp), this will be always true, port will be 22
if ($providerInfo['port'] === 22) {
$repository = "{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['repository']}";
} else {
$repository = "ssh://{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['port']}/{$providerInfo['repository']}";
}
}
}
preg_match('/(?<=:)\d+(?=\/)/', $gitRepository, $matches);
if (count($matches) === 1) {
$providerInfo['port'] = $matches[0];
$gitHost = str($gitRepository)->before(':');
$gitRepo = str($gitRepository)->after('/');
$repository = "$gitHost:$gitRepo";
}
return [
'repository' => $repository,
'port' => $providerInfo['port'],
];
}

View File

@@ -7,7 +7,7 @@ function get_socialite_provider(string $provider)
{
$oauth_setting = OauthSetting::firstWhere('provider', $provider);
if ($provider == 'azure') {
if ($provider === 'azure') {
$azure_config = new \SocialiteProviders\Manager\Config(
$oauth_setting->client_id,
$oauth_setting->client_secret,

View File

@@ -55,12 +55,11 @@ function getStripeCustomerPortalSession(Team $team)
if (! $stripe_customer_id) {
return null;
}
$session = \Stripe\BillingPortal\Session::create([
return \Stripe\BillingPortal\Session::create([
'customer' => $stripe_customer_id,
'return_url' => $return_url,
]);
return $session;
}
function allowedPathsForUnsubscribedAccounts()
{
@@ -68,7 +67,6 @@ function allowedPathsForUnsubscribedAccounts()
'subscription/new',
'login',
'logout',
'waitlist',
'force-password-reset',
'livewire/update',
];