From 02af67730666136270e2fb5ca336b048d6a0298b Mon Sep 17 00:00:00 2001 From: Matthew Knight Date: Sat, 14 Feb 2026 23:11:59 -0800 Subject: [PATCH] Fix attachments --- internal/forgejo/client.go | 22 ++++++++++++++++++++++ internal/handlers/public/tickets.go | 12 ++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/internal/forgejo/client.go b/internal/forgejo/client.go index d17e5af..ac97285 100644 --- a/internal/forgejo/client.go +++ b/internal/forgejo/client.go @@ -767,6 +767,28 @@ func (c *Client) uploadAttachment(reqURL, filename string, reader io.Reader) (*A return &attachment, nil } +// GetAttachmentURL fetches attachment metadata from the API and returns the browser_download_url. +func (c *Client) GetAttachmentURL(apiURL string) (string, error) { + resp, err := c.ProxyDownload(apiURL) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("forgejo API returned %d fetching attachment metadata", resp.StatusCode) + } + + var attachment Attachment + if err := json.NewDecoder(resp.Body).Decode(&attachment); err != nil { + return "", fmt.Errorf("failed to decode attachment metadata: %w", err) + } + if attachment.DownloadURL == "" { + return "", fmt.Errorf("attachment metadata has no browser_download_url") + } + return attachment.DownloadURL, nil +} + // ProxyDownload fetches a file from the given Forgejo URL with authentication and streams it back. func (c *Client) ProxyDownload(downloadURL string) (*http.Response, error) { httpReq, err := http.NewRequest("GET", downloadURL, nil) diff --git a/internal/handlers/public/tickets.go b/internal/handlers/public/tickets.go index 1a5f79e..bb8c1b8 100644 --- a/internal/handlers/public/tickets.go +++ b/internal/handlers/public/tickets.go @@ -505,9 +505,17 @@ func (h *TicketHandler) verifyTicketOwnership(c *gin.Context) (*models.Ticket, * // proxyAssetDownload fetches an asset from Forgejo API and streams it to the client. func (h *TicketHandler) proxyAssetDownload(c *gin.Context, assetURL, filename string) { - resp, err := h.deps.ForgejoClient.ProxyDownload(assetURL) + // First, resolve the actual download URL from the API metadata endpoint. + downloadURL, err := h.deps.ForgejoClient.GetAttachmentURL(assetURL) if err != nil { - log.Error().Err(err).Str("url", assetURL).Msg("proxy attachment download error") + log.Error().Err(err).Str("url", assetURL).Msg("failed to resolve attachment download URL") + h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusBadGateway, "Failed to download file") + return + } + + resp, err := h.deps.ForgejoClient.ProxyDownload(downloadURL) + if err != nil { + log.Error().Err(err).Str("url", downloadURL).Msg("proxy attachment download error") h.deps.Renderer.RenderError(c.Writer, c.Request, http.StatusBadGateway, "Failed to download file") return }