feat: implement security headers and rate limiting; update environment variables and documentation
This commit is contained in:
@@ -10,9 +10,6 @@ export default function HomePage() {
|
||||
<Link href="/inventory" style={{ padding: '0.5rem', background: '#eee', borderRadius: '4px', textDecoration: 'none', color: '#222' }}>
|
||||
Gå till varor som finns hemma
|
||||
</Link>
|
||||
<Link href="/admin/products" style={{ padding: '0.5rem', background: '#eee', borderRadius: '4px', textDecoration: 'none', color: '#222' }}>
|
||||
Gå till produktadmin
|
||||
</Link>
|
||||
<Link href="/recipes" style={{ padding: '0.5rem', background: '#eee', borderRadius: '4px', textDecoration: 'none', color: '#222' }}>
|
||||
Gå till recept
|
||||
</Link>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useTransition } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useAuthFetch } from '../../../lib/use-auth-fetch';
|
||||
import type {
|
||||
@@ -96,7 +96,8 @@ export default function RecipeDetailClient({ recipe: initialRecipe }: { recipe:
|
||||
// Inventarieförhandsgranskning
|
||||
const [preview, setPreview] = useState<RecipeInventoryPreview | null>(null);
|
||||
const [previewError, setPreviewError] = useState<string | null>(null);
|
||||
const [isPreviewing, startPreviewTransition] = useTransition();
|
||||
const [isPreviewing, setIsPreviewing] = useState(false);
|
||||
const previewSectionRef = useRef<HTMLElement>(null);
|
||||
|
||||
// Bilduppdatering
|
||||
const [imageUrlInput, setImageUrlInput] = useState('');
|
||||
@@ -124,17 +125,23 @@ export default function RecipeDetailClient({ recipe: initialRecipe }: { recipe:
|
||||
};
|
||||
|
||||
// ── Inventarieförhandsgranskning ──
|
||||
const loadPreview = () => {
|
||||
const loadPreview = async () => {
|
||||
if (preview) {
|
||||
previewSectionRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
return;
|
||||
}
|
||||
setPreviewError(null);
|
||||
startPreviewTransition(async () => {
|
||||
try {
|
||||
const res = await fetch(`/api/recipe-preview-proxy?id=${recipe.id}`, { cache: 'no-store' });
|
||||
if (!res.ok) throw new Error(await parseErrorResponse(res));
|
||||
setPreview(await res.json());
|
||||
} catch (err) {
|
||||
setPreviewError(err instanceof Error ? err.message : 'Fel vid hämtning av inventariedata');
|
||||
}
|
||||
});
|
||||
setIsPreviewing(true);
|
||||
try {
|
||||
const res = await fetch(`/api/recipe-preview-proxy?id=${recipe.id}`, { cache: 'no-store' });
|
||||
if (!res.ok) throw new Error(await parseErrorResponse(res));
|
||||
setPreview(await res.json());
|
||||
setTimeout(() => previewSectionRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }), 50);
|
||||
} catch (err) {
|
||||
setPreviewError(err instanceof Error ? err.message : 'Fel vid hämtning av inventariedata');
|
||||
} finally {
|
||||
setIsPreviewing(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Ta bort recept ──
|
||||
@@ -313,7 +320,7 @@ export default function RecipeDetailClient({ recipe: initialRecipe }: { recipe:
|
||||
|
||||
{/* Lagergranskning */}
|
||||
{(preview || previewError) && (
|
||||
<section style={{ ...sectionStyle, marginTop: '1.5rem' }}>
|
||||
<section ref={previewSectionRef} style={{ ...sectionStyle, marginTop: '1.5rem' }}>
|
||||
<h2 style={sectionTitle}>🛒 Vad behöver jag köpa?</h2>
|
||||
{previewError && <p style={errorStyle}>{previewError}</p>}
|
||||
{preview && (
|
||||
|
||||
Reference in New Issue
Block a user