fix(teams): allow deletion with unused private keys (#11499)
This commit is contained in:
parent
c5b7f726b0
commit
ac4f5af7c0
3 changed files with 64 additions and 8 deletions
|
|
@ -275,13 +275,22 @@ public function invitations()
|
|||
return $this->hasMany(TeamInvitation::class);
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
/**
|
||||
* @return array<string, int>
|
||||
*/
|
||||
public function deletionBlockers(): array
|
||||
{
|
||||
if ($this->projects()->count() === 0 && $this->servers()->count() === 0 && $this->privateKeys()->count() === 0 && $this->sources()->count() === 0) {
|
||||
return true;
|
||||
}
|
||||
return array_filter([
|
||||
'projects' => $this->projects()->count(),
|
||||
'servers' => $this->servers()->count(),
|
||||
'sources' => GithubApp::query()->where('team_id', $this->id)->count()
|
||||
+ GitlabApp::query()->where('team_id', $this->id)->count(),
|
||||
]);
|
||||
}
|
||||
|
||||
return false;
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this->deletionBlockers() === [];
|
||||
}
|
||||
|
||||
public function projects()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
<div>
|
||||
@php
|
||||
$deletionBlockers = currentTeam()->deletionBlockers();
|
||||
$blockerDetails = [
|
||||
'projects' => ['label' => 'project', 'route' => 'project.index'],
|
||||
'servers' => ['label' => 'server', 'route' => 'server.index'],
|
||||
'sources' => ['label' => 'Git source', 'route' => 'source.all'],
|
||||
];
|
||||
@endphp
|
||||
<x-slot:title>
|
||||
Team Danger Zone | Coolify
|
||||
</x-slot>
|
||||
|
|
@ -34,7 +42,7 @@ class="rounded-lg border border-red-300 bg-red-50 p-4 ring-1 ring-inset ring-red
|
|||
{{ wireNavigate() }} href="{{ route('subscription.show') }}">subscription</a>
|
||||
before deleting this team.
|
||||
</p>
|
||||
@elseif(currentTeam()->isEmpty())
|
||||
@elseif($deletionBlockers === [])
|
||||
<p class="mt-2 max-w-2xl text-[13px] leading-5 text-neutral-600 dark:text-fg-dim">
|
||||
Permanently delete <strong class="font-semibold text-black dark:text-fg">{{ currentTeam()->name }}</strong>
|
||||
from Coolify. This action cannot be undone.
|
||||
|
|
@ -45,7 +53,20 @@ class="rounded-lg border border-red-300 bg-red-50 p-4 ring-1 ring-inset ring-red
|
|||
</ul>
|
||||
@else
|
||||
<p class="mt-2 text-[13px] leading-5 text-neutral-600 dark:text-fg-dim">
|
||||
Remove or move every resource owned by this team before deleting it.
|
||||
This team still owns:
|
||||
</p>
|
||||
<ul class="mt-2 space-y-1 text-[13px] text-neutral-600 dark:text-fg-dim">
|
||||
@foreach ($deletionBlockers as $type => $count)
|
||||
<li>
|
||||
<a class="font-medium text-coollabs hover:underline dark:text-warning"
|
||||
{{ wireNavigate() }} href="{{ route($blockerDetails[$type]['route']) }}">
|
||||
{{ $count }} {{ str($blockerDetails[$type]['label'])->plural($count) }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<p class="mt-2 text-[13px] leading-5 text-neutral-600 dark:text-fg-dim">
|
||||
Remove or move these resources before deleting the team.
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -57,7 +78,7 @@ class="rounded-lg border border-red-300 bg-red-50 p-4 ring-1 ring-inset ring-red
|
|||
auth()->user()->teams()->count() > 1 &&
|
||||
!auth()->user()->currentTeam()->personal_team &&
|
||||
!currentTeam()->subscription &&
|
||||
currentTeam()->isEmpty())
|
||||
$deletionBlockers === [])
|
||||
<x-modal-confirmation title="Confirm Team Deletion?" buttonTitle="Delete team"
|
||||
isErrorButton submitAction="delete"
|
||||
:actions="['The current team will be permanently deleted from Coolify and the database.']"
|
||||
|
|
|
|||
|
|
@ -59,6 +59,32 @@
|
|||
->assertSuccessful();
|
||||
});
|
||||
|
||||
test('unused private keys do not block team deletion', function () {
|
||||
$privateKey = PrivateKey::factory()->create([
|
||||
'team_id' => $this->teamToDelete->id,
|
||||
'description' => 'Created by Coolify',
|
||||
]);
|
||||
|
||||
expect($this->teamToDelete->isEmpty())->toBeTrue();
|
||||
|
||||
app(DeleteTeam::class)->handle($this->teamToDelete, $this->owner);
|
||||
|
||||
expect(Team::find($this->teamToDelete->id))->toBeNull()
|
||||
->and(PrivateKey::find($privateKey->id))->toBeNull();
|
||||
});
|
||||
|
||||
test('the danger zone names and links blocking resource types', function () {
|
||||
Project::factory()->count(2)->create(['team_id' => $this->teamToDelete->id]);
|
||||
|
||||
$this->actingAs($this->owner);
|
||||
session(['currentTeam' => $this->teamToDelete]);
|
||||
|
||||
Livewire::test(DangerZone::class)
|
||||
->assertSee('This team still owns:')
|
||||
->assertSee('2 projects')
|
||||
->assertSeeHtml('href="'.route('project.index').'"');
|
||||
});
|
||||
|
||||
test('a team with a running application cannot be deleted', function () {
|
||||
$server = Server::factory()->create(['team_id' => $this->teamToDelete->id]);
|
||||
$destination = StandaloneDocker::query()->where('server_id', $server->id)->firstOrFail();
|
||||
|
|
|
|||
Loading…
Reference in a new issue