feat: add "cooked it" photo reviews

Users can rate a recipe with review text and an optional photo. Adds
ratings.photo_key column, a reviews list endpoint, and a review-purpose
presign path (reviewer isn't the recipe owner, so the upload
authorization differs from cover-photo uploads).

Also fixes CSP connect-src/img-src to allow the storage origin —
direct-to-S3/MinIO presigned uploads and stored images were silently
blocked by Content-Security-Policy in the browser.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-09 15:20:05 +02:00
parent a8406e9963
commit f2a0c20f07
12 changed files with 4507 additions and 11 deletions
+11 -1
View File
@@ -26,6 +26,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/comp
import { ServingScaler } from "@/components/recipe/serving-scaler";
import { FavoriteButton } from "@/components/social/favorite-button";
import { RatingStars } from "@/components/social/rating-stars";
import { CookedItReview } from "@/components/social/cooked-it-review";
import { CommentsSection } from "@/components/social/comments-section";
import { getPublicUrl } from "@/lib/storage";
import { cn } from "@/lib/utils";
@@ -386,7 +387,16 @@ export default async function RecipePage({ params }: Params) {
<>
<Separator />
<div className="space-y-4">
<RatingStars recipeId={id} initialScore={myScore} />
{isOwner ? (
<RatingStars recipeId={id} initialScore={myScore} readonly />
) : (
<CookedItReview
recipeId={id}
initialScore={myScore}
initialText={myRating?.reviewText ?? ""}
initialPhotoKey={myRating?.photoKey ?? null}
/>
)}
</div>
<Separator />
<CommentsSection recipeId={id} currentUserId={session.user.id} />