diff --git a/.github/workflows/remove-labels-and-assignees-on-close.yml b/.github/workflows/remove-labels-and-assignees-on-close.yml new file mode 100644 index 000000000..04d62623c --- /dev/null +++ b/.github/workflows/remove-labels-and-assignees-on-close.yml @@ -0,0 +1,75 @@ +name: Remove Labels and Assignees on Issue Close + +on: + issues: + types: [closed] + pull_request: + types: [closed] + pull_request_target: + types: [closed] + +jobs: + remove-labels-and-assignees: + runs-on: ubuntu-latest + steps: + - name: Remove labels and assignees + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { owner, repo } = context.repo; + + async function processIssue(issueNumber) { + try { + const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({ + owner, + repo, + issue_number: issueNumber + }); + + const labelsToKeep = currentLabels + .filter(label => label.name === '⏱︎ Stale') + .map(label => label.name); + + await github.rest.issues.setLabels({ + owner, + repo, + issue_number: issueNumber, + labels: labelsToKeep + }); + + const { data: issue } = await github.rest.issues.get({ + owner, + repo, + issue_number: issueNumber + }); + + if (issue.assignees && issue.assignees.length > 0) { + await github.rest.issues.removeAssignees({ + owner, + repo, + issue_number: issueNumber, + assignees: issue.assignees.map(assignee => assignee.login) + }); + } + } catch (error) { + if (error.status !== 404) { + console.error(`Error processing issue ${issueNumber}:`, error); + } + } + } + + if (context.eventName === 'issues' || context.eventName === 'pull_request' || context.eventName === 'pull_request_target') { + const issue = context.payload.issue || context.payload.pull_request; + await processIssue(issue.number); + } + + if (context.eventName === 'pull_request' || context.eventName === 'pull_request_target') { + const { data: closedIssues } = await github.rest.search.issuesAndPullRequests({ + q: `repo:${owner}/${repo} is:issue is:closed linked:${context.payload.pull_request.number}`, + per_page: 100 + }); + for (const issue of closedIssues.items) { + await processIssue(issue.number); + } + } diff --git a/app/Jobs/PullHelperImageJob.php b/app/Jobs/PullHelperImageJob.php index 420119069..63b7fa920 100644 --- a/app/Jobs/PullHelperImageJob.php +++ b/app/Jobs/PullHelperImageJob.php @@ -42,8 +42,8 @@ class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue $current_version = $settings->helper_version; if (version_compare($latest_version, $current_version, '>')) { // New version available - $helperImage = config('coolify.helper_image'); - instant_remote_process(["docker pull -q {$helperImage}:{$latest_version}"], $this->server); + // $helperImage = config('coolify.helper_image'); + // instant_remote_process(["docker pull -q {$helperImage}:{$latest_version}"], $this->server); $settings->update(['helper_version' => $latest_version]); } } diff --git a/app/Livewire/Project/Service/Navbar.php b/app/Livewire/Project/Service/Navbar.php index 674182df5..e6bb6d9bf 100644 --- a/app/Livewire/Project/Service/Navbar.php +++ b/app/Livewire/Project/Service/Navbar.php @@ -20,6 +20,8 @@ class Navbar extends Component public $isDeploymentProgress = false; + public $title = 'Configuration'; + public function mount() { if (str($this->service->status())->contains('running') && is_null($this->service->config_hash)) { diff --git a/app/Livewire/Project/Shared/ExecuteContainerCommand.php b/app/Livewire/Project/Shared/ExecuteContainerCommand.php index 79f32ab8b..d95443621 100644 --- a/app/Livewire/Project/Shared/ExecuteContainerCommand.php +++ b/app/Livewire/Project/Shared/ExecuteContainerCommand.php @@ -33,6 +33,9 @@ class ExecuteContainerCommand extends Component public function mount() { + if (! auth()->user()->isAdmin()) { + abort(403); + } $this->parameters = get_route_parameters(); $this->containers = collect(); $this->servers = collect(); @@ -130,7 +133,6 @@ class ExecuteContainerCommand extends Component { try { $container_name = data_get($this->container, 'container.Names'); - ray($this->container); if (is_null($container_name)) { throw new \RuntimeException('Container not found.'); } diff --git a/app/Livewire/Project/Shared/Terminal.php b/app/Livewire/Project/Shared/Terminal.php index 7c23c291d..802e65a30 100644 --- a/app/Livewire/Project/Shared/Terminal.php +++ b/app/Livewire/Project/Shared/Terminal.php @@ -14,13 +14,6 @@ class Terminal extends Component $server = Server::ownedByCurrentTeam()->whereUuid($serverUuid)->firstOrFail(); - // if (auth()->user()) { - // $teams = auth()->user()->teams->pluck('id'); - // if (! $teams->contains($server->team_id) && ! $teams->contains(0)) { - // throw new \Exception('User is not part of the team that owns this server'); - // } - // } - if ($isContainer) { $status = getContainerStatus($server, $identifier); if ($status !== 'running') { diff --git a/app/Livewire/RunCommand.php b/app/Livewire/RunCommand.php deleted file mode 100644 index 290618bef..000000000 --- a/app/Livewire/RunCommand.php +++ /dev/null @@ -1,101 +0,0 @@ -servers = $servers; - $this->containers = $this->getAllActiveContainers(); - } - - private function getAllActiveContainers() - { - return collect($this->servers)->flatMap(function ($server) { - if (! $server->isFunctional()) { - return []; - } - - return $server->definedResources() - ->filter(function ($resource) { - $status = method_exists($resource, 'realStatus') ? $resource->realStatus() : (method_exists($resource, 'status') ? $resource->status() : 'exited'); - - return str_starts_with($status, 'running:'); - }) - ->map(function ($resource) use ($server) { - if (isDev()) { - if (data_get($resource, 'name') === 'coolify-db') { - $container_name = 'coolify-db'; - - return [ - 'name' => $resource->name, - 'connection_name' => $container_name, - 'uuid' => $resource->uuid, - 'status' => 'running', - 'server' => $server, - 'server_uuid' => $server->uuid, - ]; - } - } - - if (class_basename($resource) === 'Application') { - if (! $server->isSwarm()) { - $current_containers = getCurrentApplicationContainerStatus($server, $resource->id, includePullrequests: true); - } - $status = $resource->status; - } elseif (class_basename($resource) === 'Service') { - $current_containers = getCurrentServiceContainerStatus($server, $resource->id); - $status = $resource->status(); - } else { - $status = getContainerStatus($server, $resource->uuid); - if ($status === 'running') { - $current_containers = collect([ - 'Names' => $resource->name, - ]); - } - } - if ($server->isSwarm()) { - $container_name = $resource->uuid.'_'.$resource->uuid; - } else { - $container_name = data_get($current_containers->first(), 'Names'); - } - - return [ - 'name' => $resource->name, - 'connection_name' => $container_name, - 'uuid' => $resource->uuid, - 'status' => $status, - 'server' => $server, - 'server_uuid' => $server->uuid, - ]; - }); - }); - } - - public function updatedSelectedUuid($value) - { - $this->connectToContainer(); - } - - #[On('connectToContainer')] - public function connectToContainer() - { - $container = collect($this->containers)->firstWhere('uuid', $this->selected_uuid); - - $this->dispatch('send-terminal-command', - isset($container), - $container['connection_name'] ?? $this->selected_uuid, - $container['server_uuid'] ?? $this->selected_uuid - ); - } -} diff --git a/app/Livewire/Terminal/Index.php b/app/Livewire/Terminal/Index.php index 3f777a8ff..945b25714 100644 --- a/app/Livewire/Terminal/Index.php +++ b/app/Livewire/Terminal/Index.php @@ -3,15 +3,70 @@ namespace App\Livewire\Terminal; use App\Models\Server; +use Livewire\Attributes\On; use Livewire\Component; class Index extends Component { + public $selected_uuid = 'default'; + public $servers = []; + public $containers = []; + public function mount() { + if (! auth()->user()->isAdmin()) { + abort(403); + } $this->servers = Server::isReachable()->get(); + $this->containers = $this->getAllActiveContainers(); + } + + private function getAllActiveContainers() + { + return collect($this->servers)->flatMap(function ($server) { + if (! $server->isFunctional()) { + return []; + } + + return $server->loadAllContainers()->map(function ($container) use ($server) { + $state = data_get_str($container, 'State')->lower(); + if ($state->contains('running')) { + return [ + 'name' => data_get($container, 'Names'), + 'connection_name' => data_get($container, 'Names'), + 'uuid' => data_get($container, 'Names'), + 'status' => data_get_str($container, 'State')->lower(), + 'server' => $server, + 'server_uuid' => $server->uuid, + ]; + } + + return null; + })->filter(); + }); + } + + public function updatedSelectedUuid() + { + $this->connectToContainer(); + } + + #[On('connectToContainer')] + public function connectToContainer() + { + if ($this->selected_uuid === 'default') { + $this->dispatch('error', 'Please select a server or a container.'); + + return; + } + $container = collect($this->containers)->firstWhere('uuid', $this->selected_uuid); + $this->dispatch('send-terminal-command', + isset($container), + $container['connection_name'] ?? $this->selected_uuid, + $container['server_uuid'] ?? $this->selected_uuid + ); } public function render() diff --git a/app/Models/Server.php b/app/Models/Server.php index 3a11c2206..90e6ade14 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -413,7 +413,7 @@ $schema://$host { handle /app/* { reverse_proxy coolify-realtime:6001 } - handle /terminal/ws/* { + handle /terminal/ws { reverse_proxy coolify-realtime:6002 } reverse_proxy coolify:80 @@ -775,6 +775,18 @@ $schema://$host { } } + public function loadAllContainers(): Collection + { + if ($this->isFunctional()) { + $containers = instant_remote_process(["docker ps -a --format '{{json .}}'"], $this); + $containers = format_docker_command_output_to_json($containers); + + return collect($containers); + } + + return collect([]); + } + public function loadUnmanagedContainers(): Collection { if ($this->isFunctional()) { diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index abfde7135..cd2779466 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -2985,7 +2985,11 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int // Get magic environments where we need to preset the FQDN if ($key->startsWith('SERVICE_FQDN_')) { // SERVICE_FQDN_APP or SERVICE_FQDN_APP_3000 - $fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value(); + if (substr_count(str($key)->value(), '_') === 3) { + $fqdnFor = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower()->value(); + } else { + $fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value(); + } if ($isApplication) { $fqdn = generateFqdn($server, "{$resource->name}-$uuid"); } elseif ($isService) { diff --git a/config/sentry.php b/config/sentry.php index 8fb0e5cdd..471a1e0fc 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -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.337', + 'release' => '4.0.0-beta.341', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index aac06d60d..32eb01cd0 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Scheduled Tasks - Terminal -
- -
diff --git a/resources/views/livewire/project/service/navbar.blade.php b/resources/views/livewire/project/service/navbar.blade.php index 125f9121a..8193fba57 100644 --- a/resources/views/livewire/project/service/navbar.blade.php +++ b/resources/views/livewire/project/service/navbar.blade.php @@ -6,17 +6,21 @@ -

Configuration

+

{{ $title }}