['type' => 'integer'], 'application_id' => ['type' => 'string'], 'deployment_uuid' => ['type' => 'string'], 'pull_request_id' => ['type' => 'integer'], 'force_rebuild' => ['type' => 'boolean'], 'commit' => ['type' => 'string'], 'status' => ['type' => 'string'], 'is_webhook' => ['type' => 'boolean'], 'is_api' => ['type' => 'boolean'], 'created_at' => ['type' => 'string'], 'updated_at' => ['type' => 'string'], 'logs' => ['type' => 'string'], 'current_process_id' => ['type' => 'string'], 'restart_only' => ['type' => 'boolean'], 'git_type' => ['type' => 'string'], 'server_id' => ['type' => 'integer'], 'application_name' => ['type' => 'string'], 'server_name' => ['type' => 'string'], 'deployment_url' => ['type' => 'string'], 'destination_id' => ['type' => 'string'], 'only_this_server' => ['type' => 'boolean'], 'rollback' => ['type' => 'boolean'], 'commit_message' => ['type' => 'string'], ], )] class ApplicationDeploymentQueue extends Model { protected $guarded = []; public function application(): Attribute { return Attribute::make( get: fn () => Application::find($this->application_id), ); } public function server(): Attribute { return Attribute::make( get: fn () => Server::find($this->server_id), ); } public function setStatus(string $status) { $this->update([ 'status' => $status, ]); } public function getOutput($name) { if (! $this->logs) { return null; } return collect(json_decode($this->logs))->where('name', $name)->first()?->output ?? null; } public function getHorizonJobStatus() { return getJobStatus($this->horizon_job_id); } public function commitMessage() { if (empty($this->commit_message) || is_null($this->commit_message)) { return null; } return str($this->commit_message)->value(); } private function redactSensitiveInfo($text) { $text = remove_iip($text); $app = $this->application; if (! $app) { return $text; } $lockedVars = collect([]); if ($app->environment_variables) { $lockedVars = $lockedVars->merge( $app->environment_variables ->where('is_shown_once', true) ->pluck('real_value', 'key') ->filter() ); } if ($this->pull_request_id !== 0 && $app->environment_variables_preview) { $lockedVars = $lockedVars->merge( $app->environment_variables_preview ->where('is_shown_once', true) ->pluck('real_value', 'key') ->filter() ); } foreach ($lockedVars as $key => $value) { $escapedValue = preg_quote($value, '/'); $text = preg_replace( '/'.$escapedValue.'/', REDACTED, $text ); } return $text; } public function addLogEntry(string $message, string $type = 'stdout', bool $hidden = false) { if ($type === 'error') { $type = 'stderr'; } $message = str($message)->trim(); if ($message->startsWith('╔')) { $message = "\n".$message; } $newLogEntry = [ 'command' => null, 'output' => $this->redactSensitiveInfo($message), 'type' => $type, 'timestamp' => Carbon::now('UTC'), 'hidden' => $hidden, 'batch' => 1, ]; // Use a transaction to ensure atomicity DB::transaction(function () use ($newLogEntry) { // Reload the model to get the latest logs $this->refresh(); if ($this->logs) { $previousLogs = json_decode($this->logs, associative: true, flags: JSON_THROW_ON_ERROR); $newLogEntry['order'] = count($previousLogs) + 1; $previousLogs[] = $newLogEntry; $this->logs = json_encode($previousLogs, flags: JSON_THROW_ON_ERROR); } else { $this->logs = json_encode([$newLogEntry], flags: JSON_THROW_ON_ERROR); } // Save without triggering events to prevent potential race conditions $this->saveQuietly(); }); } }