From b92c39d9f0eab1b4f715dd0fd514798ae97e4d60 Mon Sep 17 00:00:00 2001
From: sebastjan
Date: Sat, 15 Aug 2026 17:06:09 +0200
Subject: [PATCH] Dnevni recept: tabela daily_recipes, API /api/recipes, prikaz
na /rezepte
---
client/src/pages/recipes.tsx | 47 ++++++++++++++++++++++++++++++++++++
server/daily-recipe.ts | 39 ++++++++++++++++++++++++++++++
server/routes.ts | 21 ++++++++++++++++
shared/schema.ts | 11 +++++++++
4 files changed, 118 insertions(+)
create mode 100644 server/daily-recipe.ts
diff --git a/client/src/pages/recipes.tsx b/client/src/pages/recipes.tsx
index f177c87..036e7df 100644
--- a/client/src/pages/recipes.tsx
+++ b/client/src/pages/recipes.tsx
@@ -1,4 +1,5 @@
import { useState } from "react";
+import { useQuery } from "@tanstack/react-query";
import { Link } from "wouter";
import { ChefHat, Clock, Users, X, ChevronLeft } from "lucide-react";
import { usePageMeta } from "@/hooks/use-page-meta";
@@ -295,6 +296,50 @@ function RecipeModal({ recipe, onClose }: { recipe: Recipe; onClose: () => void
);
}
+interface DailyRecipe {
+ name: string;
+ intro?: string;
+ recipe?: string;
+ seasonNote?: string | null;
+ imageUrl?: string | null;
+}
+
+function RecipeOfTheDay() {
+ const { data } = useQuery({
+ queryKey: ["/api/recipes/today"],
+ staleTime: 1000 * 60 * 30,
+ });
+ if (!data || !data.name) return null;
+ return (
+
+
+
+ {data.imageUrl && (
+

+ )}
+
+
{data.name}
+ {data.intro &&
{data.intro}
}
+ {data.recipe && (
+
{data.recipe}
+ )}
+ {data.seasonNote &&
{data.seasonNote}
}
+
+
+
+ );
+}
+
export default function RecipesPage() {
usePageMeta("Rezepte - Alpenküche & Volksmusik", "Traditionelle Rezepte aus der Alpenküche und österreichische Hausmannskost. Kochen wie die Volksmusik-Stars bei FOLX TV.");
const [selectedRecipe, setSelectedRecipe] = useState(null);
@@ -320,6 +365,8 @@ export default function RecipesPage() {
Traditionelle Rezepte aus den germanischen Regionen -- von der Alm bis zur Küste, von der Oma überliefert.
+
+
{RECIPE_REGIONS.map((region, ri) => (
diff --git a/server/daily-recipe.ts b/server/daily-recipe.ts
new file mode 100644
index 0000000..4b87e7d
--- /dev/null
+++ b/server/daily-recipe.ts
@@ -0,0 +1,39 @@
+import { db } from "./db";
+import { dailyRecipes } from "@shared/schema";
+import { eq } from "drizzle-orm";
+
+function todayStr(): string {
+ const d = new Date();
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
+}
+
+export async function getRecipeForToday(): Promise
{
+ const rows = await db.select().from(dailyRecipes).where(eq(dailyRecipes.dateStr, todayStr()));
+ return rows.length > 0 ? rows[0] : null;
+}
+
+export async function upsertRecipe(data: {
+ dateStr?: string;
+ name: string;
+ intro?: string;
+ recipe?: string;
+ seasonNote?: string | null;
+ imageUrl?: string | null;
+}): Promise {
+ const dateStr = data.dateStr || todayStr();
+ const values = {
+ dateStr,
+ name: data.name,
+ intro: data.intro || "",
+ recipe: data.recipe || "",
+ seasonNote: data.seasonNote || null,
+ imageUrl: data.imageUrl || null,
+ };
+ const existing = await db.select().from(dailyRecipes).where(eq(dailyRecipes.dateStr, dateStr));
+ if (existing.length > 0) {
+ const [row] = await db.update(dailyRecipes).set(values).where(eq(dailyRecipes.id, existing[0].id)).returning();
+ return row;
+ }
+ const [row] = await db.insert(dailyRecipes).values(values).returning();
+ return row;
+}
diff --git a/server/routes.ts b/server/routes.ts
index 9cb7e54..0578556 100644
--- a/server/routes.ts
+++ b/server/routes.ts
@@ -4,6 +4,7 @@ import { storage } from "./storage";
import { insertArticleSchema } from "@shared/schema";
import { seedDatabase } from "./seed";
import { generateDailyHoroscopes, getHoroscopesForToday, getOrGenerateHoroscope, setHoroscopeImages } from "./horoscope-generator";
+import { getRecipeForToday, upsertRecipe } from "./daily-recipe";
import { startDailyScheduler } from "./scheduler";
import { analyzeAllArticleImages, getCachedFocalPoints } from "./focal-point";
import { optimizeImage } from "./image-optimizer";
@@ -754,6 +755,26 @@ export async function registerRoutes(
}
});
+ app.get("/api/recipes/today", async (_req, res) => {
+ try {
+ const recipe = await getRecipeForToday();
+ res.json(recipe);
+ } catch (err: any) {
+ res.status(500).json({ message: err.message });
+ }
+ });
+
+ app.post("/api/recipes", requireApiKey, async (req, res) => {
+ try {
+ const { name } = req.body || {};
+ if (!name) return res.status(400).json({ message: "name ist erforderlich" });
+ const row = await upsertRecipe(req.body);
+ res.json(row);
+ } catch (err: any) {
+ res.status(500).json({ message: err.message });
+ }
+ });
+
app.get("/api/horoscopes/today", async (_req, res) => {
try {
const horoscopes = await getHoroscopesForToday();
diff --git a/shared/schema.ts b/shared/schema.ts
index acdc822..b9918ab 100644
--- a/shared/schema.ts
+++ b/shared/schema.ts
@@ -34,6 +34,17 @@ export const articleViews = pgTable("article_views", {
viewedAt: timestamp("viewed_at").notNull().defaultNow(),
});
+export const dailyRecipes = pgTable("daily_recipes", {
+ id: serial("id").primaryKey(),
+ dateStr: varchar("date_str", { length: 10 }).notNull(),
+ name: text("name").notNull(),
+ intro: text("intro").notNull().default(""),
+ recipe: text("recipe").notNull().default(""),
+ seasonNote: text("season_note"),
+ imageUrl: text("image_url"),
+ createdAt: timestamp("created_at").notNull().defaultNow(),
+});
+
export const dailyHoroscopes = pgTable("daily_horoscopes", {
id: serial("id").primaryKey(),
signIndex: integer("sign_index").notNull(),