Merge pull request #4470 from coollabsio/rename-github-app
Feat: Ability to rename GitHub App
This commit is contained in:
		@@ -463,7 +463,7 @@ class Github extends Controller
 | 
				
			|||||||
            $private_key = data_get($data, 'pem');
 | 
					            $private_key = data_get($data, 'pem');
 | 
				
			||||||
            $webhook_secret = data_get($data, 'webhook_secret');
 | 
					            $webhook_secret = data_get($data, 'webhook_secret');
 | 
				
			||||||
            $private_key = PrivateKey::create([
 | 
					            $private_key = PrivateKey::create([
 | 
				
			||||||
                'name' => $slug,
 | 
					                'name' => "github-app-{$slug}",
 | 
				
			||||||
                'private_key' => $private_key,
 | 
					                'private_key' => $private_key,
 | 
				
			||||||
                'team_id' => $github_app->team_id,
 | 
					                'team_id' => $github_app->team_id,
 | 
				
			||||||
                'is_git_related' => true,
 | 
					                'is_git_related' => true,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,11 @@ namespace App\Livewire\Source\Github;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Jobs\GithubAppPermissionJob;
 | 
					use App\Jobs\GithubAppPermissionJob;
 | 
				
			||||||
use App\Models\GithubApp;
 | 
					use App\Models\GithubApp;
 | 
				
			||||||
 | 
					use App\Models\PrivateKey;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Http;
 | 
				
			||||||
 | 
					use Lcobucci\JWT\Configuration;
 | 
				
			||||||
 | 
					use Lcobucci\JWT\Signer\Key\InMemory;
 | 
				
			||||||
 | 
					use Lcobucci\JWT\Signer\Rsa\Sha256;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Change extends Component
 | 
					class Change extends Component
 | 
				
			||||||
@@ -51,12 +56,20 @@ class Change extends Component
 | 
				
			|||||||
        'github_app.administration' => 'nullable|string',
 | 
					        'github_app.administration' => 'nullable|string',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function boot()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->github_app) {
 | 
				
			||||||
 | 
					            $this->github_app->makeVisible(['client_secret', 'webhook_secret']);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function checkPermissions()
 | 
					    public function checkPermissions()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        GithubAppPermissionJob::dispatchSync($this->github_app);
 | 
					        GithubAppPermissionJob::dispatchSync($this->github_app);
 | 
				
			||||||
        $this->github_app->refresh()->makeVisible('client_secret')->makeVisible('webhook_secret');
 | 
					        $this->github_app->refresh()->makeVisible('client_secret')->makeVisible('webhook_secret');
 | 
				
			||||||
        $this->dispatch('success', 'Github App permissions updated.');
 | 
					        $this->dispatch('success', 'Github App permissions updated.');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // public function check()
 | 
					    // public function check()
 | 
				
			||||||
    // {
 | 
					    // {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -90,15 +103,16 @@ class Change extends Component
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    //     ray($runners_by_repository);
 | 
					    //     ray($runners_by_repository);
 | 
				
			||||||
    // }
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $github_app_uuid = request()->github_app_uuid;
 | 
					            $github_app_uuid = request()->github_app_uuid;
 | 
				
			||||||
            $this->github_app = GithubApp::ownedByCurrentTeam()->whereUuid($github_app_uuid)->firstOrFail();
 | 
					            $this->github_app = GithubApp::ownedByCurrentTeam()->whereUuid($github_app_uuid)->firstOrFail();
 | 
				
			||||||
 | 
					            $this->github_app->makeVisible(['client_secret', 'webhook_secret']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $this->applications = $this->github_app->applications;
 | 
					            $this->applications = $this->github_app->applications;
 | 
				
			||||||
            $settings = instanceSettings();
 | 
					            $settings = instanceSettings();
 | 
				
			||||||
            $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $this->name = str($this->github_app->name)->kebab();
 | 
					            $this->name = str($this->github_app->name)->kebab();
 | 
				
			||||||
            $this->fqdn = $settings->fqdn;
 | 
					            $this->fqdn = $settings->fqdn;
 | 
				
			||||||
@@ -142,6 +156,77 @@ class Change extends Component
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getGithubAppNameUpdatePath()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (str($this->github_app->organization)->isNotEmpty()) {
 | 
				
			||||||
 | 
					            return "{$this->github_app->html_url}/organizations/{$this->github_app->organization}/settings/apps/{$this->github_app->name}";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return "{$this->github_app->html_url}/settings/apps/{$this->github_app->name}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generateGithubJwt($private_key, $app_id): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $configuration = Configuration::forAsymmetricSigner(
 | 
				
			||||||
 | 
					            new Sha256,
 | 
				
			||||||
 | 
					            InMemory::plainText($private_key),
 | 
				
			||||||
 | 
					            InMemory::plainText($private_key)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $now = time();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $configuration->builder()
 | 
				
			||||||
 | 
					            ->issuedBy((string) $app_id)
 | 
				
			||||||
 | 
					            ->permittedFor('https://api.github.com')
 | 
				
			||||||
 | 
					            ->identifiedBy((string) $now)
 | 
				
			||||||
 | 
					            ->issuedAt(new \DateTimeImmutable("@{$now}"))
 | 
				
			||||||
 | 
					            ->expiresAt(new \DateTimeImmutable('@'.($now + 600)))
 | 
				
			||||||
 | 
					            ->getToken($configuration->signer(), $configuration->signingKey())
 | 
				
			||||||
 | 
					            ->toString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function updateGithubAppName()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $privateKey = PrivateKey::ownedByCurrentTeam()->find($this->github_app->private_key_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (! $privateKey) {
 | 
				
			||||||
 | 
					                $this->dispatch('error', 'No private key found for this GitHub App.');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $jwt = $this->generateGithubJwt($privateKey->private_key, $this->github_app->app_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $response = Http::withHeaders([
 | 
				
			||||||
 | 
					                'Accept' => 'application/vnd.github+json',
 | 
				
			||||||
 | 
					                'X-GitHub-Api-Version' => '2022-11-28',
 | 
				
			||||||
 | 
					                'Authorization' => "Bearer {$jwt}",
 | 
				
			||||||
 | 
					            ])->get("{$this->github_app->api_url}/app");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($response->successful()) {
 | 
				
			||||||
 | 
					                $app_data = $response->json();
 | 
				
			||||||
 | 
					                $app_slug = $app_data['slug'] ?? null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($app_slug) {
 | 
				
			||||||
 | 
					                    $this->github_app->name = $app_slug;
 | 
				
			||||||
 | 
					                    $this->name = str($app_slug)->kebab();
 | 
				
			||||||
 | 
					                    $privateKey->name = "github-app-{$app_slug}";
 | 
				
			||||||
 | 
					                    $privateKey->save();
 | 
				
			||||||
 | 
					                    $this->github_app->save();
 | 
				
			||||||
 | 
					                    $this->dispatch('success', 'GitHub App name and SSH key name synchronized successfully.');
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $this->dispatch('info', 'Could not find App Name (slug) in GitHub response.');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $error_message = $response->json()['message'] ?? 'Unknown error';
 | 
				
			||||||
 | 
					                $this->dispatch('error', "Failed to fetch GitHub App information: {$error_message}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ class GithubAppSeeder extends Seeder
 | 
				
			|||||||
        GithubApp::create([
 | 
					        GithubApp::create([
 | 
				
			||||||
            'name' => 'coolify-laravel-development-public',
 | 
					            'name' => 'coolify-laravel-development-public',
 | 
				
			||||||
            'uuid' => '69420',
 | 
					            'uuid' => '69420',
 | 
				
			||||||
 | 
					            'organization' => 'coollabsio',
 | 
				
			||||||
            'api_url' => 'https://api.github.com',
 | 
					            'api_url' => 'https://api.github.com',
 | 
				
			||||||
            'html_url' => 'https://github.com',
 | 
					            'html_url' => 'https://github.com',
 | 
				
			||||||
            'is_public' => false,
 | 
					            'is_public' => false,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,7 +58,18 @@
 | 
				
			|||||||
            @else
 | 
					            @else
 | 
				
			||||||
                <div class="flex flex-col gap-2">
 | 
					                <div class="flex flex-col gap-2">
 | 
				
			||||||
                    <div class="flex gap-2">
 | 
					                    <div class="flex gap-2">
 | 
				
			||||||
 | 
					                        <div class="flex items-end gap-2 w-full">
 | 
				
			||||||
                            <x-forms.input id="github_app.name" label="App Name" disabled />
 | 
					                            <x-forms.input id="github_app.name" label="App Name" disabled />
 | 
				
			||||||
 | 
					                            <x-forms.button wire:click.prevent="updateGithubAppName" class="bg-coollabs">
 | 
				
			||||||
 | 
					                                Sync Name
 | 
				
			||||||
 | 
					                            </x-forms.button>
 | 
				
			||||||
 | 
					                            <a href="{{ $this->getGithubAppNameUpdatePath() }}">
 | 
				
			||||||
 | 
					                                <x-forms.button>
 | 
				
			||||||
 | 
					                                    Rename
 | 
				
			||||||
 | 
					                                    <x-external-link />
 | 
				
			||||||
 | 
					                                </x-forms.button>
 | 
				
			||||||
 | 
					                            </a>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
                        <x-forms.input id="github_app.organization" label="Organization" disabled
 | 
					                        <x-forms.input id="github_app.organization" label="Organization" disabled
 | 
				
			||||||
                            placeholder="If empty, personal user will be used" />
 | 
					                            placeholder="If empty, personal user will be used" />
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user