mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-15 22:14:53 +00:00
Merge remote-tracking branch 'origin/next' into v5-parallel-inertia-react
This commit is contained in:
commit
c9416abec6
117 files changed
+5121
-795
No files matched your search
@@ -52,6 +52,21 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private const RAILPACK_GENERATED_CONFIG_PATH = '.coolify/railpack.generated.json';
|
||||
|
||||
private const DOCKER_CLIENT_ENV_KEYS = [
|
||||
'BUILDKIT_HOST',
|
||||
'BUILDX_BUILDER',
|
||||
'BUILDX_CONFIG',
|
||||
'DOCKER_API_VERSION',
|
||||
'DOCKER_BUILDKIT',
|
||||
'DOCKER_CERT_PATH',
|
||||
'DOCKER_CLI_EXPERIMENTAL',
|
||||
'DOCKER_CONFIG',
|
||||
'DOCKER_CONTEXT',
|
||||
'DOCKER_HOST',
|
||||
'DOCKER_TLS',
|
||||
'DOCKER_TLS_VERIFY',
|
||||
];
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public $timeout = 3600;
|
||||
@@ -1031,7 +1046,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
);
|
||||
}
|
||||
foreach ($this->application->fileStorages as $fileStorage) {
|
||||
if (! $fileStorage->is_based_on_git && ! $fileStorage->is_directory) {
|
||||
if (! $fileStorage->is_host_file && ! $fileStorage->is_based_on_git && ! $fileStorage->is_directory) {
|
||||
$fileStorage->saveStorageOnServer();
|
||||
}
|
||||
}
|
||||
@@ -1701,6 +1716,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
|
||||
foreach ($sorted_environment_variables as $env) {
|
||||
if ($this->build_pack === 'railpack' && $this->is_reserved_docker_client_env_key($env->key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$resolvedValue = $env->getResolvedValueWithServer($this->mainServer);
|
||||
// For literal/multiline vars, real_value includes quotes that we need to remove
|
||||
if ($env->is_literal || $env->is_multiline) {
|
||||
@@ -1752,6 +1771,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
|
||||
foreach ($sorted_environment_variables as $env) {
|
||||
if ($this->build_pack === 'railpack' && $this->is_reserved_docker_client_env_key($env->key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$resolvedValue = $env->getResolvedValueWithServer($this->mainServer);
|
||||
// For literal/multiline vars, real_value includes quotes that we need to remove
|
||||
if ($env->is_literal || $env->is_multiline) {
|
||||
@@ -2124,7 +2147,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
private function prepare_builder_image(bool $firstTry = true)
|
||||
{
|
||||
$this->checkForCancellation();
|
||||
$helperImage = config('constants.coolify.helper_image');
|
||||
$helperImage = coolifyHelperImage();
|
||||
$helperImage = "{$helperImage}:".getHelperVersion();
|
||||
// Get user home directory
|
||||
$this->serverUserHomeDir = instant_remote_process(['echo $HOME'], $this->server);
|
||||
@@ -2229,7 +2252,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
// Only include SOURCE_COMMIT in build context if enabled in settings
|
||||
if ($this->application->settings->include_source_commit_in_build) {
|
||||
$this->coolify_variables .= "SOURCE_COMMIT={$this->commit} ";
|
||||
$this->coolify_variables .= 'SOURCE_COMMIT='.escapeShellValue($this->commit).' ';
|
||||
}
|
||||
if ($this->pull_request_id === 0) {
|
||||
$fqdn = $this->application->fqdn;
|
||||
@@ -2241,17 +2264,33 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$fqdn = $url->getHost();
|
||||
$url = $url->withHost($fqdn)->withPort(null)->__toString();
|
||||
if ((int) $this->application->compose_parsing_version >= 3) {
|
||||
$this->coolify_variables .= "COOLIFY_URL={$url} ";
|
||||
$this->coolify_variables .= "COOLIFY_FQDN={$fqdn} ";
|
||||
$this->coolify_variables .= 'COOLIFY_URL='.escapeShellValue($url).' ';
|
||||
$this->coolify_variables .= 'COOLIFY_FQDN='.escapeShellValue($fqdn).' ';
|
||||
} else {
|
||||
$this->coolify_variables .= "COOLIFY_URL={$fqdn} ";
|
||||
$this->coolify_variables .= "COOLIFY_FQDN={$url} ";
|
||||
$this->coolify_variables .= 'COOLIFY_URL='.escapeShellValue($fqdn).' ';
|
||||
$this->coolify_variables .= 'COOLIFY_FQDN='.escapeShellValue($url).' ';
|
||||
}
|
||||
}
|
||||
if (isset($this->application->git_branch)) {
|
||||
$this->coolify_variables .= 'COOLIFY_BRANCH='.escapeShellValue($this->application->git_branch).' ';
|
||||
}
|
||||
$this->coolify_variables .= "COOLIFY_RESOURCE_UUID={$this->application->uuid} ";
|
||||
$this->coolify_variables .= 'COOLIFY_RESOURCE_UUID='.escapeShellValue($this->application->uuid).' ';
|
||||
}
|
||||
|
||||
private function shellAssignmentForDockerfileArg(string $assignment): string
|
||||
{
|
||||
[$key, $value] = array_pad(explode('=', $assignment, 2), 2, null);
|
||||
|
||||
if ($value === null) {
|
||||
return $assignment;
|
||||
}
|
||||
|
||||
if (str_starts_with($value, "'") && str_ends_with($value, "'")) {
|
||||
$value = substr($value, 1, -1);
|
||||
$value = str_replace("'\\''", "'", $value);
|
||||
}
|
||||
|
||||
return "{$key}={$value}";
|
||||
}
|
||||
|
||||
private function gitLsRemoteCommand(string $lsRemoteRef, ?string $identityFile = null): string
|
||||
@@ -2327,7 +2366,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
],
|
||||
);
|
||||
}
|
||||
if ($this->saved_outputs->get('git_commit_sha') && ! $this->rollback) {
|
||||
if ($this->saved_outputs->get('git_commit_sha') && ! $this->rollback && $this->shouldResolveBranchHeadCommit()) {
|
||||
// Extract commit SHA from git ls-remote output, handling multi-line output (e.g., redirect warnings)
|
||||
// Expected format: "commit_sha\trefs/heads/branch" possibly preceded by warning lines
|
||||
// Note: Git warnings can be on the same line as the result (no newline)
|
||||
@@ -2359,6 +2398,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
private function shouldResolveBranchHeadCommit(): bool
|
||||
{
|
||||
$commit = trim($this->commit);
|
||||
|
||||
return $commit === '' || $commit === 'HEAD';
|
||||
}
|
||||
|
||||
private function clone_repository()
|
||||
{
|
||||
$importCommands = $this->generate_git_import_commands();
|
||||
@@ -2566,6 +2612,20 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->env_nixpacks_args = $this->env_nixpacks_args->implode(' ');
|
||||
}
|
||||
|
||||
private function is_reserved_docker_client_env_key(?string $key): bool
|
||||
{
|
||||
if (blank($key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array(strtoupper($key), self::DOCKER_CLIENT_ENV_KEYS, true);
|
||||
}
|
||||
|
||||
private function without_reserved_docker_client_variables(Collection $variables): Collection
|
||||
{
|
||||
return $variables->reject(fn ($value, $key) => $this->is_reserved_docker_client_env_key((string) $key));
|
||||
}
|
||||
|
||||
private function generate_railpack_env_variables(): Collection
|
||||
{
|
||||
$variables = $this->railpack_build_variables();
|
||||
@@ -2686,6 +2746,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function railpack_build_command(string $imageName, Collection $variables): string
|
||||
{
|
||||
$variables = $this->without_reserved_docker_client_variables($variables);
|
||||
|
||||
$cacheArgs = '';
|
||||
if ($this->force_rebuild) {
|
||||
$cacheArgs = '--no-cache';
|
||||
@@ -2712,7 +2774,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$secretFlags = $this->railpack_build_secret_flags($variables);
|
||||
$frontendImage = 'ghcr.io/railwayapp/railpack-frontend:v'.config('constants.coolify.railpack_version');
|
||||
|
||||
$buildxBuildCommand = "{$environmentPrefix}docker buildx build --builder coolify-railpack"
|
||||
$buildxBuildCommand = "{$environmentPrefix}DOCKER_CONFIG=/root/.docker docker buildx build --builder coolify-railpack"
|
||||
." {$this->addHosts} --network host"
|
||||
." --build-arg BUILDKIT_SYNTAX=\"{$frontendImage}\""
|
||||
." {$cacheArgs}"
|
||||
@@ -2723,7 +2785,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
." -t {$imageName}"
|
||||
." {$this->workdir}";
|
||||
|
||||
return 'docker buildx create --name coolify-railpack --driver docker-container 2>/dev/null || true'
|
||||
return 'DOCKER_CONFIG=/root/.docker docker buildx create --name coolify-railpack --driver docker-container 2>/dev/null || true'
|
||||
.' && '.$this->wrap_build_command_with_env_export($buildxBuildCommand);
|
||||
}
|
||||
|
||||
@@ -2878,9 +2940,25 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
throw new DeploymentException('Railpack deployments require the Docker buildx CLI plugin on the build server. Install or enable docker buildx and retry the deployment.');
|
||||
}
|
||||
|
||||
private function ensure_helper_docker_buildx_available_for_railpack(): void
|
||||
{
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, 'DOCKER_CONFIG=/root/.docker docker buildx version >/dev/null 2>&1 && echo available || echo not-available'),
|
||||
'hidden' => true,
|
||||
'save' => 'railpack_helper_buildx_available',
|
||||
]);
|
||||
|
||||
if (trim((string) $this->saved_outputs->get('railpack_helper_buildx_available')) === 'available') {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new DeploymentException('Railpack deployments require the Docker buildx CLI plugin inside the Coolify helper container. The helper could not find buildx at /root/.docker/cli-plugins/docker-buildx. Pull the latest helper image and retry the deployment.');
|
||||
}
|
||||
|
||||
private function build_railpack_image(): void
|
||||
{
|
||||
$this->ensure_docker_buildx_available_for_railpack();
|
||||
$this->ensure_helper_docker_buildx_available_for_railpack();
|
||||
|
||||
$railpackVariables = $this->generate_railpack_env_variables();
|
||||
$railpackConfigPath = $this->generate_railpack_config_file();
|
||||
@@ -4080,6 +4158,10 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
|
||||
$variables = $this->env_args;
|
||||
|
||||
if ($this->build_pack === 'railpack') {
|
||||
$variables = $this->without_reserved_docker_client_variables($variables);
|
||||
}
|
||||
|
||||
if ($variables->isEmpty()) {
|
||||
return '';
|
||||
}
|
||||
@@ -4220,7 +4302,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
$coolify_vars = collect(explode(' ', trim($this->coolify_variables)))
|
||||
->filter()
|
||||
->map(function ($var) {
|
||||
return "ARG {$var}";
|
||||
return 'ARG '.$this->shellAssignmentForDockerfileArg($var);
|
||||
});
|
||||
$argsToInsert = $argsToInsert->merge($coolify_vars);
|
||||
}
|
||||
@@ -4242,7 +4324,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
$coolify_vars = collect(explode(' ', trim($this->coolify_variables)))
|
||||
->filter()
|
||||
->map(function ($var) {
|
||||
return "ARG {$var}";
|
||||
return 'ARG '.$this->shellAssignmentForDockerfileArg($var);
|
||||
});
|
||||
$argsToInsert = $argsToInsert->merge($coolify_vars);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, S
|
||||
'active_deployment_uuids' => $activeDeployments,
|
||||
]);
|
||||
|
||||
$containers = instant_remote_process_with_timeout(['docker container ps --format \'{{json .}}\' | jq -s \'map(select(.Image | contains("'.config('constants.coolify.registry_url').'/coollabsio/coolify-helper")))\''], $this->server, false);
|
||||
$containers = instant_remote_process_with_timeout(['docker container ps --format \'{{json .}}\' | jq -s \'map(select(.Image | contains("'.coolifyRegistryUrl().'/coollabsio/coolify-helper")))\''], $this->server, false);
|
||||
$helperContainers = collect(json_decode($containers));
|
||||
|
||||
if ($helperContainers->count() > 0) {
|
||||
|
||||
@@ -16,6 +16,7 @@ use App\Models\Team;
|
||||
use App\Notifications\Database\BackupFailed;
|
||||
use App\Notifications\Database\BackupSuccess;
|
||||
use App\Notifications\Database\BackupSuccessWithS3Warning;
|
||||
use App\Rules\SafeWebhookUrl;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
@@ -714,9 +715,15 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$escapedEndpoint = escapeshellarg($endpoint);
|
||||
$escapedKey = escapeshellarg($key);
|
||||
$escapedSecret = escapeshellarg($secret);
|
||||
$escapedBackupLocation = escapeshellarg($this->backup_location);
|
||||
$escapedS3Destination = escapeshellarg("temporary/{$bucket}{$this->backup_dir}/");
|
||||
$resolveOptions = collect(SafeWebhookUrl::minioClientResolveOptions($endpoint))
|
||||
->map(fn (string $resolveOption): string => '--resolve '.escapeshellarg($resolveOption))
|
||||
->implode(' ');
|
||||
$resolveOptions = $resolveOptions === '' ? '' : ' '.$resolveOptions;
|
||||
|
||||
$commands[] = "docker exec backup-of-{$this->backup_log_uuid} mc alias set temporary {$escapedEndpoint} {$escapedKey} {$escapedSecret}";
|
||||
$commands[] = "docker exec backup-of-{$this->backup_log_uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
||||
$commands[] = "docker exec backup-of-{$this->backup_log_uuid} mc alias set{$resolveOptions} temporary {$escapedEndpoint} {$escapedKey} {$escapedSecret}";
|
||||
$commands[] = "docker exec backup-of-{$this->backup_log_uuid} mc cp {$escapedBackupLocation} {$escapedS3Destination}";
|
||||
instant_remote_process($commands, $this->server, true, false, null, disableMultiplexing: true);
|
||||
|
||||
$this->s3_uploaded = true;
|
||||
@@ -732,7 +739,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function getFullImageName(): string
|
||||
{
|
||||
$helperImage = config('constants.coolify.helper_image');
|
||||
$helperImage = coolifyHelperImage();
|
||||
$latestVersion = getHelperVersion();
|
||||
|
||||
return "{$helperImage}:{$latestVersion}";
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Rules\SafeWebhookUrl;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@@ -10,6 +11,8 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class SendMessageToDiscordJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -41,6 +44,31 @@ class SendMessageToDiscordJob implements ShouldBeEncrypted, ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Http::post($this->webhookUrl, $this->message->toPayload());
|
||||
$validator = Validator::make(
|
||||
['webhook_url' => $this->webhookUrl],
|
||||
['webhook_url' => ['required', 'url', new SafeWebhookUrl]]
|
||||
);
|
||||
|
||||
if ($validator->fails()) {
|
||||
Log::warning('SendMessageToDiscordJob: blocked unsafe webhook URL', [
|
||||
'url' => SafeWebhookUrl::redactedUrlForLog($this->webhookUrl),
|
||||
'errors' => $validator->errors()->all(),
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$httpOptions = SafeWebhookUrl::httpClientOptions($this->webhookUrl);
|
||||
} catch (\RuntimeException $e) {
|
||||
Log::warning('SendMessageToDiscordJob: blocked unsafe webhook URL at send time', [
|
||||
'url' => SafeWebhookUrl::redactedUrlForLog($this->webhookUrl),
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Http::withOptions($httpOptions)->post($this->webhookUrl, $this->message->toPayload());
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use App\Rules\SafeWebhookUrl;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@@ -10,6 +11,8 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class SendMessageToSlackJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -34,8 +37,33 @@ class SendMessageToSlackJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$validator = Validator::make(
|
||||
['webhook_url' => $this->webhookUrl],
|
||||
['webhook_url' => ['required', 'url', new SafeWebhookUrl]]
|
||||
);
|
||||
|
||||
if ($validator->fails()) {
|
||||
Log::warning('SendMessageToSlackJob: blocked unsafe webhook URL', [
|
||||
'url' => SafeWebhookUrl::redactedUrlForLog($this->webhookUrl),
|
||||
'errors' => $validator->errors()->all(),
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$httpOptions = SafeWebhookUrl::httpClientOptions($this->webhookUrl);
|
||||
} catch (\RuntimeException $e) {
|
||||
Log::warning('SendMessageToSlackJob: blocked unsafe webhook URL at send time', [
|
||||
'url' => SafeWebhookUrl::redactedUrlForLog($this->webhookUrl),
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isSlackWebhook()) {
|
||||
$this->sendToSlack();
|
||||
$this->sendToSlack($httpOptions);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -45,7 +73,7 @@ class SendMessageToSlackJob implements ShouldBeEncrypted, ShouldQueue
|
||||
*
|
||||
* @see https://github.com/coollabsio/coolify/pull/6139#issuecomment-3756777708
|
||||
*/
|
||||
$this->sendToMattermost();
|
||||
$this->sendToMattermost($httpOptions);
|
||||
}
|
||||
|
||||
private function isSlackWebhook(): bool
|
||||
@@ -62,9 +90,12 @@ class SendMessageToSlackJob implements ShouldBeEncrypted, ShouldQueue
|
||||
return $scheme === 'https' && $host === 'hooks.slack.com';
|
||||
}
|
||||
|
||||
private function sendToSlack(): void
|
||||
/**
|
||||
* @param array<string, mixed> $httpOptions
|
||||
*/
|
||||
private function sendToSlack(array $httpOptions): void
|
||||
{
|
||||
Http::post($this->webhookUrl, [
|
||||
Http::withOptions($httpOptions)->post($this->webhookUrl, [
|
||||
'text' => $this->message->title,
|
||||
'blocks' => [
|
||||
[
|
||||
@@ -102,11 +133,14 @@ class SendMessageToSlackJob implements ShouldBeEncrypted, ShouldQueue
|
||||
/**
|
||||
* @todo v5 refactor: Extract this into a separate SendMessageToMattermostJob.php triggered via the "mattermost" notification channel type.
|
||||
*/
|
||||
private function sendToMattermost(): void
|
||||
/**
|
||||
* @param array<string, mixed> $httpOptions
|
||||
*/
|
||||
private function sendToMattermost(array $httpOptions): void
|
||||
{
|
||||
$username = config('app.name');
|
||||
|
||||
Http::post($this->webhookUrl, [
|
||||
Http::withOptions($httpOptions)->post($this->webhookUrl, [
|
||||
'username' => $username,
|
||||
'attachments' => [
|
||||
[
|
||||
|
||||
@@ -50,7 +50,7 @@ class SendWebhookJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
if ($validator->fails()) {
|
||||
Log::warning('SendWebhookJob: blocked unsafe webhook URL', [
|
||||
'url' => $this->webhookUrl,
|
||||
'url' => SafeWebhookUrl::redactedUrlForLog($this->webhookUrl),
|
||||
'errors' => $validator->errors()->all(),
|
||||
]);
|
||||
|
||||
@@ -64,7 +64,18 @@ class SendWebhookJob implements ShouldBeEncrypted, ShouldQueue
|
||||
]);
|
||||
}
|
||||
|
||||
$response = Http::post($this->webhookUrl, $this->payload);
|
||||
try {
|
||||
$httpOptions = SafeWebhookUrl::httpClientOptions($this->webhookUrl);
|
||||
} catch (\RuntimeException $e) {
|
||||
Log::warning('SendWebhookJob: blocked unsafe webhook URL at send time', [
|
||||
'url' => SafeWebhookUrl::redactedUrlForLog($this->webhookUrl),
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$response = Http::withOptions($httpOptions)->post($this->webhookUrl, $this->payload);
|
||||
|
||||
if (isDev()) {
|
||||
ray('Webhook response', [
|
||||
|
||||
Reference in new issue
Block a user