From 92d0b066f13971d61e1bbb12e347301756823e49 Mon Sep 17 00:00:00 2001 From: Nils-Johan Gynther Date: Sun, 19 Apr 2026 21:46:09 +0200 Subject: [PATCH] fix: skicka Authorization-header i klientens direktanrop till /api/recipes Caddy routar /api/recipes* direkt till NestJS (bypassing Next.js). Klient-komponenter maste lagga till JWT-token sjalva via useSession(). Pavaerkade filer: - RecipeDetailClient.tsx (PATCH, DELETE, POST image) - ImportRecipePage.tsx (POST /api/recipes) - WriteRecipePage.tsx (POST /api/recipes) --- frontend/app/recipes/[id]/RecipeDetailClient.tsx | 10 +++++++--- frontend/app/recipes/import/ImportRecipePage.tsx | 4 +++- frontend/app/recipes/write/WriteRecipePage.tsx | 4 +++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/frontend/app/recipes/[id]/RecipeDetailClient.tsx b/frontend/app/recipes/[id]/RecipeDetailClient.tsx index 7c5502f1..58f6e32a 100644 --- a/frontend/app/recipes/[id]/RecipeDetailClient.tsx +++ b/frontend/app/recipes/[id]/RecipeDetailClient.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useTransition } from 'react'; import { useRouter } from 'next/navigation'; +import { useSession } from 'next-auth/react'; import type { Recipe, Product, @@ -64,6 +65,7 @@ function StatusBadge({ status }: { status: 'enough' | 'missing' | 'unit_mismatch export default function RecipeDetailClient({ recipe: initialRecipe }: { recipe: Recipe }) { const router = useRouter(); + const { data: session } = useSession(); const [recipe, setRecipe] = useState(initialRecipe); const [isEditing, setIsEditing] = useState(false); const [isLiked, setIsLiked] = useState(false); @@ -140,7 +142,9 @@ export default function RecipeDetailClient({ recipe: initialRecipe }: { recipe: if (!confirm(`Ta bort receptet "${recipe.name}"? Det går inte att ångra.`)) return; setIsDeleting(true); try { - const res = await fetch(`/api/recipes/${recipe.id}`, { method: 'DELETE' }); + const res = await fetch(`/api/recipes/${recipe.id}`, { method: 'DELETE', + headers: { Authorization: `Bearer ${session?.accessToken}` }, + }); if (!res.ok) throw new Error(await parseErrorResponse(res)); router.push('/recipes'); } catch (err) { @@ -164,7 +168,7 @@ export default function RecipeDetailClient({ recipe: initialRecipe }: { recipe: }; const res = await fetch(`/api/recipes/${recipe.id}`, { method: 'PATCH', - headers: { 'Content-Type': 'application/json' }, + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${session?.accessToken}` }, body: JSON.stringify(body), }); if (!res.ok) throw new Error(await parseErrorResponse(res)); @@ -186,7 +190,7 @@ export default function RecipeDetailClient({ recipe: initialRecipe }: { recipe: try { const res = await fetch(`/api/recipes/${recipe.id}/image`, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${session?.accessToken}` }, body: JSON.stringify({ sourceUrl: imageUrlInput.trim() }), }); if (!res.ok) throw new Error(await parseErrorResponse(res)); diff --git a/frontend/app/recipes/import/ImportRecipePage.tsx b/frontend/app/recipes/import/ImportRecipePage.tsx index ed3b147b..b684e29d 100644 --- a/frontend/app/recipes/import/ImportRecipePage.tsx +++ b/frontend/app/recipes/import/ImportRecipePage.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; +import { useSession } from 'next-auth/react'; import { fetchJson } from '../../../lib/api'; import { parseErrorResponse } from '../../../lib/error-handler'; import type { Product } from '../../../features/inventory/types'; @@ -37,6 +38,7 @@ type Step = 'input' | 'review' | 'saving'; export default function ImportRecipePage() { const router = useRouter(); + const { data: session } = useSession(); const [step, setStep] = useState('input'); const [markdown, setMarkdown] = useState(''); const [parsed, setParsed] = useState(null); @@ -136,7 +138,7 @@ export default function ImportRecipePage() { try { const res = await fetch('/api/recipes', { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${session?.accessToken}` }, body: JSON.stringify(body), }); diff --git a/frontend/app/recipes/write/WriteRecipePage.tsx b/frontend/app/recipes/write/WriteRecipePage.tsx index 6b5b135f..112c9cdf 100644 --- a/frontend/app/recipes/write/WriteRecipePage.tsx +++ b/frontend/app/recipes/write/WriteRecipePage.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; +import { useSession } from 'next-auth/react'; import { fetchJson } from '../../../lib/api'; import { parseErrorResponse } from '../../../lib/error-handler'; import type { Product } from '../../../features/inventory/types'; @@ -36,6 +37,7 @@ type Step = 'input' | 'review' | 'saving' | 'saved'; export default function WriteRecipePage() { const router = useRouter(); + const { data: session } = useSession(); const [step, setStep] = useState('input'); const [markdown, setMarkdown] = useState(''); const [parsed, setParsed] = useState(null); @@ -193,7 +195,7 @@ export default function WriteRecipePage() { try { const res = await fetch('/api/recipes', { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${session?.accessToken}` }, body: JSON.stringify(body), });