diff --git a/internal/api/diff_review.go b/internal/api/diff_review.go index 1d98f3e..4a5bc04 100644 --- a/internal/api/diff_review.go +++ b/internal/api/diff_review.go @@ -202,6 +202,11 @@ func (s *Server) DiffReview(c echo.Context) error { func (s *Server) GetDiffReviewStatus(c echo.Context) error { // API key authentication is handled by middleware + orgID, ok := c.Get("org_id").(int64) + if !ok || orgID == 0 { + return JSONErrorWithEnvelope(c, http.StatusUnauthorized, "missing org context") + } + reviewIDStr := c.Param("review_id") reviewID, err := strconv.ParseInt(reviewIDStr, 10, 64) if err != nil { @@ -209,7 +214,7 @@ func (s *Server) GetDiffReviewStatus(c echo.Context) error { } rm := NewReviewManager(s.db) - reviewRecord, err := rm.GetReview(reviewID) + reviewRecord, err := rm.GetReviewForOrg(reviewID, orgID) if err != nil { return JSONErrorWithEnvelope(c, http.StatusNotFound, "review not found") } @@ -274,7 +279,8 @@ func (s *Server) GetDiffReviewStatus(c echo.Context) error { preloaded, err := decodePreloadedChanges(meta) if err != nil { - return JSONErrorWithEnvelope(c, http.StatusInternalServerError, fmt.Sprintf("failed to decode preloaded changes: %v", err)) + log.Printf("[WARN] preloaded_changes unavailable for review %d, serving without code context: %v", reviewID, err) + preloaded = nil } result, err := decodeReviewResult(meta) @@ -316,6 +322,7 @@ func (s *Server) runDiffReview(request review.ReviewRequest, rm *ReviewManager, // Attach event sink so logs go to review_events table for UI eventSink := NewDatabaseEventSink(s.db) logger.SetEventSink(eventSink) + defer logger.Close() logger.LogSection("CLI DIFF REVIEW STARTED") logger.Log("Review ID: %d", reviewID) logger.Log("Organization ID: %d", orgID) @@ -410,6 +417,7 @@ func (s *Server) runDiffReview(request review.ReviewRequest, rm *ReviewManager, } if logger != nil { logger.LogSection("REVIEW COMPLETED") + logger.Log("Review ID: %d", reviewID) logger.Log("Successfully generated %d comments", len(result.Comments)) } } else { @@ -434,10 +442,6 @@ func (s *Server) runDiffReview(request review.ReviewRequest, rm *ReviewManager, } } - if err := rm.UpdateReviewStatus(reviewID, status); err != nil { - log.Printf("[WARN] failed to update review status for %d: %v", reviewID, err) - } - payload := DiffReviewResult{Summary: summary, Comments: comments} meta := map[string]interface{}{"review_result": payload} if failureReason != "" { @@ -447,6 +451,10 @@ func (s *Server) runDiffReview(request review.ReviewRequest, rm *ReviewManager, log.Printf("[WARN] failed to persist review_result for %d: %v", reviewID, err) } + if err := rm.UpdateReviewStatus(reviewID, status); err != nil { + log.Printf("[WARN] failed to update review status for %d: %v", reviewID, err) + } + // Persist AI summary title for later display (extract first heading only) if summary != "" { title := extractFirstHeading(summary) diff --git a/internal/api/reviews.go b/internal/api/reviews.go index 776aad1..8150bda 100644 --- a/internal/api/reviews.go +++ b/internal/api/reviews.go @@ -221,6 +221,59 @@ func (rm *ReviewManager) GetReview(reviewID int64) (*Review, error) { return &review, nil } +// GetReviewForOrg retrieves a review by ID scoped to a specific org. +// Returns an error if the review does not exist or belongs to a different org. +func (rm *ReviewManager) GetReviewForOrg(reviewID int64, orgID int64) (*Review, error) { + query := ` + SELECT id, repository, branch, commit_hash, pr_mr_url, connector_id, + status, trigger_type, user_email, provider, mr_title, friendly_name, author_name, author_username, + created_at, started_at, completed_at, metadata + FROM reviews + WHERE id = $1 AND org_id = $2 + ` + + var review Review + var mrTitle, friendlyName, authorName, authorUsername sql.NullString + err := rm.store.QueryRow(query, reviewID, orgID).Scan( + &review.ID, + &review.Repository, + &review.Branch, + &review.CommitHash, + &review.PrMrURL, + &review.ConnectorID, + &review.Status, + &review.TriggerType, + &review.UserEmail, + &review.Provider, + &mrTitle, + &friendlyName, + &authorName, + &authorUsername, + &review.CreatedAt, + &review.StartedAt, + &review.CompletedAt, + &review.Metadata, + ) + if err != nil { + return nil, fmt.Errorf("failed to get review: %w", err) + } + + if mrTitle.Valid { + review.MRTitle = &mrTitle.String + } + if friendlyName.Valid { + review.FriendlyName = &friendlyName.String + } + if authorName.Valid { + review.AuthorName = &authorName.String + } + if authorUsername.Valid { + review.AuthorUsername = &authorUsername.String + } + + return &review, nil +} + // ReviewMetadataUpdate describes optional fields that can be updated on a review record. type ReviewMetadataUpdate struct { Repository *string diff --git a/ui/package-lock.json b/ui/package-lock.json index ce0c8cf..03dd4a4 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -16542,9 +16542,9 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { diff --git a/ui/package.json b/ui/package.json index 0c1a359..94cc2ea 100644 --- a/ui/package.json +++ b/ui/package.json @@ -134,7 +134,7 @@ "picomatch@2": "2.3.2", "picomatch@4": "4.0.4", "preact": "10.27.3", - "qs": "6.14.2", + "qs": "6.15.2", "react-router": "6.30.2", "serialize-javascript": "7.0.5", "svgo": "3.3.3",