diff --git a/internal/api/handlers/github.go b/internal/api/handlers/github.go index f0785c7..16cf190 100644 --- a/internal/api/handlers/github.go +++ b/internal/api/handlers/github.go @@ -256,7 +256,8 @@ func (h *GitHubHandler) ManifestCallback(w http.ResponseWriter, r *http.Request) } // Seamlessly redirect to the installation page on GitHub - installURL := fmt.Sprintf("https://github.com/settings/apps/%s/installations/new", *config.Slug) + // Use the correct GitHub Apps installation URL format + installURL := fmt.Sprintf("https://github.com/apps/%s/installations/new", *config.Slug) http.Redirect(w, r, installURL, http.StatusFound) } @@ -401,7 +402,8 @@ func (h *GitHubHandler) AppInstall(w http.ResponseWriter, r *http.Request) { return } - installURL := fmt.Sprintf("https://github.com/settings/apps/%s/installations/new", *config.Slug) + // Use the correct GitHub Apps installation URL format + installURL := fmt.Sprintf("https://github.com/apps/%s/installations/new", *config.Slug) WriteJSON(w, http.StatusOK, map[string]string{"url": installURL}) } @@ -414,10 +416,31 @@ func (h *GitHubHandler) PostInstallation(w http.ResponseWriter, r *http.Request) h.logger.Info("GitHub post-installation callback", "installation_id", installationIDStr, "setup_action", setupAction, + "host", r.Host, + "x-forwarded-host", r.Header.Get("X-Forwarded-Host"), ) + // Get the correct web URL from environment or settings + // Don't rely on request headers as GitHub is calling this endpoint + ctx := r.Context() + webURL := os.Getenv("PUBLIC_WEB_URL") + if webURL == "" { + // Try database settings + if dbDomain, _ := h.store.Settings().Get(ctx, "server_domain"); dbDomain != "" && dbDomain != "localhost" { + scheme := "http" + if r.TLS != nil { + scheme = "https" + } + webURL = fmt.Sprintf("%s://%s:8090", scheme, dbDomain) + } else { + // Fallback - try to detect from request but filter internal hostnames + webURL, _ = h.getBaseURLs(r) + } + } + + h.logger.Info("redirecting to web URL", "url", webURL) + if installationIDStr == "" { - webURL, _ := h.getBaseURLs(r) http.Redirect(w, r, webURL+"/git?error=missing_installation_id", http.StatusFound) return } @@ -434,7 +457,6 @@ func (h *GitHubHandler) PostInstallation(w http.ResponseWriter, r *http.Request) h.logger.Info("no user context in post-installation, storing orphaned installation", "installation_id", installationID) // We could store this in a temporary table or skip for now // For now, just redirect to login - webURL, _ := h.getBaseURLs(r) http.Redirect(w, r, webURL+"/git?success=GitHub+App+installed&installation_id="+installationIDStr, http.StatusFound) return } @@ -450,7 +472,6 @@ func (h *GitHubHandler) PostInstallation(w http.ResponseWriter, r *http.Request) h.logger.Error("failed to create installation", "error", err) } - webURL, _ := h.getBaseURLs(r) http.Redirect(w, r, webURL+"/git?success=GitHub+App+installed", http.StatusFound) } diff --git a/web/assets/js/ui-polish.js b/web/assets/js/ui-polish.js index e7ff220..577f127 100644 --- a/web/assets/js/ui-polish.js +++ b/web/assets/js/ui-polish.js @@ -63,11 +63,8 @@ async function initGitIntegration() { const gitSection = document.getElementById('git-connection-section'); if (!gitSection) { - // Check if we are in the detail page with a repo picker - const repoPicker = document.getElementById('github-repo-picker'); - if (repoPicker) { - loadGithubRepos(); - } + // Check if we are in the detail page with repo pickers + initRepoPickers(); return; } @@ -211,6 +208,39 @@ } // Reconfigure logic + // Add Installation button handler + const btnAddInstallation = document.getElementById('btn-connect-github-instance'); + if (btnAddInstallation) { + btnAddInstallation.addEventListener('click', async () => { + btnAddInstallation.disabled = true; + const originalHTML = btnAddInstallation.innerHTML; + btnAddInstallation.innerHTML = '⏳ Loading...'; + + try { + const response = await fetch('/api/github/install'); + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + const data = await response.json(); + + if (data.url) { + // Redirect to GitHub installation page + window.location.href = data.url; + } else if (data.error) { + throw new Error(data.error); + } else { + throw new Error('No installation URL returned'); + } + } catch (err) { + console.error('Add installation failed:', err); + alert('Error: ' + err.message); + btnAddInstallation.disabled = false; + btnAddInstallation.innerHTML = originalHTML; + } + }); + } + + // Reconfigure button handler const btnReconfigure = document.getElementById('btn-reconfigure-github'); if (btnReconfigure) { btnReconfigure.addEventListener('click', async () => { @@ -238,68 +268,134 @@ }); } - async function loadGithubRepos() { - const repoPicker = document.getElementById('github-repo-picker'); - const manualInput = document.getElementById('manual-repo-input'); - const hiddenRepo = document.getElementById('selected-github-repo'); - if (!repoPicker) return; + // Initialize repository pickers for service creation dialogs + function initRepoPickers() { + const prefixes = ['web', 'static', 'worker']; + + prefixes.forEach(prefix => { + const repoPicker = document.getElementById(`${prefix}-repo-picker`); + if (repoPicker) { + loadReposForPicker(prefix); + } + }); + + // Listen for provider tab switches + document.querySelectorAll('[data-provider-tab]').forEach(tab => { + tab.addEventListener('click', function() { + const prefix = this.getAttribute('data-provider-tab').split('-')[0]; + const provider = this.getAttribute('data-provider'); + + // Update active tab styling + document.querySelectorAll(`[data-provider-tab^="${prefix}-"]`).forEach(t => { + t.classList.remove('bg-zinc-800', 'text-white'); + t.classList.add('text-muted-foreground'); + }); + this.classList.add('bg-zinc-800', 'text-white'); + this.classList.remove('text-muted-foreground'); + + // Load repos for the selected provider + loadReposForPicker(prefix, provider); + }); + }); + } + + async function loadReposForPicker(prefix, provider = 'github') { + const selectId = `${prefix}_repo_select`; + const contentZone = document.querySelector(`#${selectId} [data-tui-selectbox-content]`); + const hiddenInput = document.getElementById(`${prefix}_selected_repo`); + const repoInput = document.getElementById(`${prefix}_svc_repo`); + + if (!contentZone) return; + + // Show loading state + contentZone.innerHTML = '
Loading repositories...
+ @label.Label(label.Props{For: prefix + "_repo_select", Class: "text-sm"}) { Repository } + @selectbox.SelectBox(selectbox.Props{ID: prefix + "_repo_select", Class: "w-full"}) { + @selectbox.Trigger() { + @selectbox.Value(selectbox.ValueProps{Placeholder: "Loading repositories..."}) + } + @selectbox.Content() { ++ Select a repository from your connected GitHub account +