feat(core): add secure recipe APIs, precise scaling and versioned imports

This commit is contained in:
OpenCode committed 2026-10-24 15:40:44 +02:00
commit 5d71d32e20
31 files changed
+6293

No files matched your search

+349
View File
@@ -0,0 +1,349 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"version": {
"type": "number",
"const": 1
},
"recipes": {
"minItems": 1,
"maxItems": 100,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"title": {
"type": "string",
"minLength": 2,
"maxLength": 180
},
"subtitle": {
"default": "",
"type": "string",
"maxLength": 200
},
"description": {
"default": "",
"type": "string",
"maxLength": 4000
},
"image": {
"default": "",
"anyOf": [
{
"type": "string",
"pattern": "^\\/(?:media\\/[a-f0-9-]+\\.webp|images\\/[a-z0-9-]+\\.(?:webp|jpg|svg))$"
},
{
"type": "string",
"const": ""
}
]
},
"images": {
"default": [],
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"pattern": "^\\/(?:media\\/[a-f0-9-]+\\.webp|images\\/[a-z0-9-]+\\.(?:webp|jpg|svg))$"
}
},
"categories": {
"default": [],
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 60
}
},
"tags": {
"default": [],
"maxItems": 30,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 60
}
},
"cuisine": {
"default": "",
"type": "string",
"maxLength": 80
},
"difficulty": {
"default": "Einfach",
"type": "string",
"enum": [
"Einfach",
"Mittel",
"Anspruchsvoll"
]
},
"prepMinutes": {
"default": 0,
"type": "integer",
"minimum": 0,
"maximum": 10080
},
"cookMinutes": {
"default": 0,
"type": "integer",
"minimum": 0,
"maximum": 10080
},
"restMinutes": {
"default": 0,
"type": "integer",
"minimum": 0,
"maximum": 10080
},
"servings": {
"type": "number",
"exclusiveMinimum": 0,
"maximum": 10000
},
"ingredients": {
"minItems": 1,
"maxItems": 200,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^[a-zA-Z0-9_-]{1,80}$"
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 160
},
"amount": {
"default": null,
"anyOf": [
{
"type": "number",
"exclusiveMinimum": 0,
"maximum": 1000000
},
{
"type": "null"
}
]
},
"unit": {
"default": "",
"type": "string",
"enum": [
"g",
"kg",
"ml",
"l",
"Stück",
"TL",
"EL",
"Prise",
"Bund",
""
]
},
"section": {
"default": "",
"type": "string",
"maxLength": 80
},
"note": {
"default": "",
"type": "string",
"maxLength": 200
},
"optional": {
"default": false,
"type": "boolean"
}
},
"required": [
"id",
"name",
"amount",
"unit",
"section",
"note",
"optional"
],
"additionalProperties": false
}
},
"steps": {
"minItems": 1,
"maxItems": 100,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^[a-zA-Z0-9_-]{1,80}$"
},
"title": {
"default": "",
"type": "string",
"maxLength": 160
},
"text": {
"type": "string",
"minLength": 1,
"maxLength": 6000
},
"ingredients": {
"default": [],
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-zA-Z0-9_-]{1,80}$"
}
},
"timers": {
"default": [],
"maxItems": 10,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^[a-zA-Z0-9_-]{1,80}$"
},
"label": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 172800
}
},
"required": [
"id",
"label",
"seconds"
],
"additionalProperties": false
}
}
},
"required": [
"id",
"title",
"text",
"ingredients",
"timers"
],
"additionalProperties": false
}
},
"tips": {
"default": "",
"type": "string",
"maxLength": 6000
},
"storage": {
"default": "",
"type": "string",
"maxLength": 3000
},
"nutrition": {
"default": null,
"anyOf": [
{
"type": "object",
"properties": {
"kcal": {
"type": "number",
"minimum": 0
},
"protein": {
"type": "number",
"minimum": 0
},
"carbs": {
"type": "number",
"minimum": 0
},
"fat": {
"type": "number",
"minimum": 0
}
},
"required": [
"kcal",
"protein",
"carbs",
"fat"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"published": {
"default": false,
"type": "boolean"
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
}
},
"required": [
"title",
"subtitle",
"description",
"image",
"images",
"categories",
"tags",
"cuisine",
"difficulty",
"prepMinutes",
"cookMinutes",
"restMinutes",
"servings",
"ingredients",
"steps",
"tips",
"storage",
"nutrition",
"published"
],
"additionalProperties": false
}
}
},
"required": [
"version",
"recipes"
],
"additionalProperties": false
}
+430
View File
@@ -0,0 +1,430 @@
{
"version": 1,
"recipes": [
{
"id": "10000000-0000-4000-8000-000000000001",
"title": "Grüne Pesto-Pasta",
"subtitle": "Ein Teller Sommer, ganz ohne Eile.",
"description": "Frisches Basilikum, geröstete Pinienkerne und ein Hauch Zitrone. Unsere liebste Pasta für Tage, an denen es einfach gut sein soll.",
"image": "/images/kitchen.jpg",
"images": [],
"categories": [
"Alltagsküche",
"Vegetarisch"
],
"tags": [
"Schnell",
"Familienliebling"
],
"cuisine": "Italien",
"difficulty": "Einfach",
"prepMinutes": 10,
"cookMinutes": 12,
"restMinutes": 0,
"servings": 2,
"ingredients": [
{
"id": "pasta",
"name": "Pasta",
"amount": 200,
"unit": "g",
"section": "Für die Pasta",
"note": "",
"optional": false
},
{
"id": "basilikum",
"name": "Basilikum",
"amount": 40,
"unit": "g",
"section": "Für das Pesto",
"note": "",
"optional": false
},
{
"id": "kerne",
"name": "Pinienkerne",
"amount": 30,
"unit": "g",
"section": "Für das Pesto",
"note": "",
"optional": false
},
{
"id": "oel",
"name": "Olivenöl",
"amount": 60,
"unit": "ml",
"section": "Für das Pesto",
"note": "",
"optional": false
},
{
"id": "kaese",
"name": "Hartkäse mit mikrobiellem Lab",
"amount": 40,
"unit": "g",
"section": "Für das Pesto",
"note": "",
"optional": false
},
{
"id": "zitrone",
"name": "Bio-Zitrone",
"amount": 0.5,
"unit": "Stück",
"section": "Zum Abschmecken",
"note": "",
"optional": true
},
{
"id": "salz",
"name": "Salz",
"amount": null,
"unit": "",
"section": "Zum Abschmecken",
"note": "nach Geschmack",
"optional": false
}
],
"steps": [
{
"id": "vorbereiten",
"title": "Ein bisschen Vorbereitung",
"text": "{{basilikum}} waschen und trocken schütteln. {{kaese}} fein reiben. Einen großen Topf Salzwasser aufsetzen.",
"ingredients": [
"basilikum",
"kaese",
"salz"
],
"timers": []
},
{
"id": "pesto",
"title": "Das Pesto zum Leuchten bringen",
"text": "{{kerne}} in einer trockenen Pfanne goldbraun rösten. Mit Basilikum und {{oel}} fein mixen, anschließend den geriebenen Käse unterheben.",
"ingredients": [
"kerne",
"basilikum",
"oel",
"kaese"
],
"timers": [
{
"id": "roesten",
"label": "Pinienkerne rösten",
"seconds": 180
}
]
},
{
"id": "kochen",
"title": "Pasta trifft Pesto",
"text": "{{pasta}} nach Packungsangabe bissfest kochen. Vor dem Abgießen etwas Kochwasser auffangen. Pasta mit dem Pesto vermengen und mit Kochwasser geschmeidig rühren.",
"ingredients": [
"pasta"
],
"timers": [
{
"id": "nudeln",
"label": "Pasta kochen",
"seconds": 600
}
]
},
{
"id": "geniessen",
"title": "Der schönste Moment",
"text": "Mit dem Saft von {{zitrone}} und Salz abschmecken. Auf warme Teller verteilen und sofort genießen.",
"ingredients": [
"zitrone",
"salz"
],
"timers": []
}
],
"tips": "Pesto nicht stark erhitzen: So bleibt es leuchtend grün. Ein Schuss stärkehaltiges Nudelwasser macht die Sauce wunderbar cremig.",
"storage": "Pesto in einem sauberen Glas mit einer dünnen Ölschicht bedecken und im Kühlschrank innerhalb von zwei Tagen verbrauchen.",
"nutrition": {
"kcal": 780,
"protein": 22,
"carbs": 79,
"fat": 42
},
"published": true
},
{
"id": "10000000-0000-4000-8000-000000000002",
"title": "Bunte Garten-Bowl",
"subtitle": "Knackig, frisch und voller guter Dinge.",
"description": "Eine großzügige Schüssel Gemüse, cremige Kichererbsen und ein zitroniges Tahini-Dressing. Genau richtig für einen frischen Start.",
"image": "/images/garden.jpg",
"images": [],
"categories": [
"Vegetarisch"
],
"tags": [
"Meal Prep"
],
"cuisine": "Mediterran",
"difficulty": "Einfach",
"prepMinutes": 20,
"cookMinutes": 10,
"restMinutes": 0,
"servings": 2,
"ingredients": [
{
"id": "kichererbsen",
"name": "Kichererbsen, gekocht",
"amount": 240,
"unit": "g",
"section": "",
"note": "",
"optional": false
},
{
"id": "salat",
"name": "Blattsalat",
"amount": 100,
"unit": "g",
"section": "",
"note": "",
"optional": false
},
{
"id": "tomaten",
"name": "Cherrytomaten",
"amount": 200,
"unit": "g",
"section": "",
"note": "",
"optional": false
},
{
"id": "gurke",
"name": "Gurke",
"amount": 0.5,
"unit": "Stück",
"section": "",
"note": "",
"optional": false
},
{
"id": "tahini",
"name": "Tahini",
"amount": 2,
"unit": "EL",
"section": "",
"note": "",
"optional": false
},
{
"id": "zitrone",
"name": "Zitronensaft",
"amount": 2,
"unit": "EL",
"section": "",
"note": "",
"optional": false
},
{
"id": "wasser",
"name": "Wasser",
"amount": 3,
"unit": "EL",
"section": "",
"note": "",
"optional": false
},
{
"id": "salz",
"name": "Salz",
"amount": null,
"unit": "",
"section": "",
"note": "nach Geschmack",
"optional": false
}
],
"steps": [
{
"id": "gemuese",
"title": "Bunt vorbereiten",
"text": "{{salat}} waschen und trocknen. {{tomaten}} halbieren und {{gurke}} in Scheiben schneiden.",
"ingredients": [
"salat",
"tomaten",
"gurke"
],
"timers": []
},
{
"id": "roesten",
"title": "Kichererbsen rösten",
"text": "{{kichererbsen}} abtropfen lassen und in einer beschichteten Pfanne zehn Minuten rösten. Regelmäßig umrühren.",
"ingredients": [
"kichererbsen"
],
"timers": [
{
"id": "roesten",
"label": "Kichererbsen rösten",
"seconds": 600
}
]
},
{
"id": "dressing",
"title": "Alles kommt zusammen",
"text": "{{tahini}} mit {{zitrone}}, {{wasser}} und Salz verrühren. Gemüse und Kichererbsen anrichten und das Dressing darübergeben.",
"ingredients": [
"tahini",
"zitrone",
"wasser",
"salz"
],
"timers": []
}
],
"tips": "Das Dressing separat aufbewahren, wenn die Bowl für morgen ist.",
"storage": "Gekühlt einen Tag haltbar. Salat und Dressing getrennt lagern.",
"nutrition": null,
"published": true
},
{
"id": "10000000-0000-4000-8000-000000000003",
"title": "Wärmende Gemüsesuppe",
"subtitle": "Ein Löffel Gemütlichkeit.",
"description": "Saisongemüse, weiße Bohnen und aromatische Kräuter machen aus wenigen Zutaten ein wohliges Essen für die ganze Familie.",
"image": "/images/soup.jpg",
"images": [],
"categories": [
"Alltagsküche"
],
"tags": [
"Familienliebling",
"Meal Prep"
],
"cuisine": "",
"difficulty": "Einfach",
"prepMinutes": 15,
"cookMinutes": 30,
"restMinutes": 0,
"servings": 4,
"ingredients": [
{
"id": "karotten",
"name": "Karotten",
"amount": 300,
"unit": "g",
"section": "",
"note": "",
"optional": false
},
{
"id": "kartoffeln",
"name": "Kartoffeln",
"amount": 400,
"unit": "g",
"section": "",
"note": "",
"optional": false
},
{
"id": "bohnen",
"name": "Weiße Bohnen, gekocht",
"amount": 240,
"unit": "g",
"section": "",
"note": "",
"optional": false
},
{
"id": "bruehe",
"name": "Gemüsebrühe",
"amount": 1,
"unit": "l",
"section": "",
"note": "",
"optional": false
},
{
"id": "zwiebel",
"name": "Zwiebel",
"amount": 1,
"unit": "Stück",
"section": "",
"note": "",
"optional": false
},
{
"id": "oel",
"name": "Olivenöl",
"amount": 1,
"unit": "EL",
"section": "",
"note": "",
"optional": false
},
{
"id": "kraeuter",
"name": "Petersilie",
"amount": 0.5,
"unit": "Bund",
"section": "",
"note": "",
"optional": true
}
],
"steps": [
{
"id": "schneiden",
"title": "Alles klein schneiden",
"text": "{{karotten}}, {{kartoffeln}} und {{zwiebel}} schälen und würfeln.",
"ingredients": [
"karotten",
"kartoffeln",
"zwiebel"
],
"timers": []
},
{
"id": "kochen",
"title": "Zeit für Wärme",
"text": "Zwiebel in {{oel}} glasig dünsten. Karotten und Kartoffeln dazugeben, mit {{bruehe}} aufgießen und zugedeckt etwa 25 Minuten sanft kochen, bis das Gemüse weich ist.",
"ingredients": [
"oel",
"bruehe"
],
"timers": [
{
"id": "suppe",
"label": "Gemüse weich kochen",
"seconds": 1500
}
]
},
{
"id": "abschmecken",
"title": "Der letzte Schliff",
"text": "{{bohnen}} dazugeben und fünf Minuten erwärmen. Abschmecken und mit {{kraeuter}} bestreuen.",
"ingredients": [
"bohnen",
"kraeuter"
],
"timers": [
{
"id": "bohnen",
"label": "Bohnen erwärmen",
"seconds": 300
}
]
}
],
"tips": "",
"storage": "Rasch abkühlen und im Kühlschrank maximal zwei Tage lagern. Portionsweise bis zu drei Monate einfrieren.",
"nutrition": null,
"published": true
}
]
}
+167
View File
@@ -0,0 +1,167 @@
{
"version": 1,
"recipes": [
{
"title": "Serbische Pljeskavica",
"subtitle": "Saftig gegrillt, mit Ajvar und frischen Zwiebeln",
"description": "Ein Klassiker vom Balkan: würzige Faschierte-Laibchen, außen kräftig geröstet und innen saftig. Dazu passen Lepinja, Ajvar und ein knackiger Salat.",
"image": "",
"images": [],
"categories": [
"Für Gäste"
],
"tags": [
"Grillen",
"Familienliebling"
],
"cuisine": "Serbien",
"difficulty": "Einfach",
"prepMinutes": 20,
"cookMinutes": 12,
"restMinutes": 60,
"servings": 4,
"ingredients": [
{
"id": "fleisch",
"name": "Rinderfaschiertes",
"amount": 800,
"unit": "g",
"section": "Pljeskavica",
"note": "",
"optional": false
},
{
"id": "zwiebel",
"name": "Zwiebel",
"amount": 1,
"unit": "Stück",
"section": "Pljeskavica",
"note": "sehr fein gewürfelt",
"optional": false
},
{
"id": "paprika",
"name": "Paprikapulver edelsüß",
"amount": 2,
"unit": "TL",
"section": "Pljeskavica",
"note": "",
"optional": false
},
{
"id": "salz",
"name": "Salz",
"amount": 10,
"unit": "g",
"section": "Pljeskavica",
"note": "",
"optional": false
},
{
"id": "pfeffer",
"name": "Schwarzer Pfeffer",
"amount": null,
"unit": "",
"section": "Pljeskavica",
"note": "nach Geschmack",
"optional": false
},
{
"id": "wasser",
"name": "Mineralwasser",
"amount": 60,
"unit": "ml",
"section": "Pljeskavica",
"note": "",
"optional": false
},
{
"id": "ajvar",
"name": "Ajvar",
"amount": 120,
"unit": "g",
"section": "Zum Servieren",
"note": "",
"optional": true
},
{
"id": "brot",
"name": "Lepinja",
"amount": 4,
"unit": "Stück",
"section": "Zum Servieren",
"note": "",
"optional": false
}
],
"steps": [
{
"id": "mischen",
"title": "Die Mischung vorbereiten",
"text": "{{fleisch}}, {{zwiebel}}, {{paprika}}, {{salz}} und {{wasser}} gründlich verkneten. Mit {{pfeffer}} abschmecken.",
"ingredients": [
"fleisch",
"zwiebel",
"paprika",
"salz",
"wasser",
"pfeffer"
],
"timers": []
},
{
"id": "ruhen",
"title": "Geschmack entwickeln lassen",
"text": "Die Masse zugedeckt mindestens eine Stunde im Kühlschrank ruhen lassen.",
"ingredients": [],
"timers": [
{
"id": "ruhezeit",
"label": "Im Kühlschrank ruhen",
"seconds": 3600
}
]
},
{
"id": "formen",
"title": "Flache Laibchen formen",
"text": "Mit angefeuchteten Händen je Portion ein etwa 1,5 cm dickes Laibchen formen. In die Mitte eine leichte Mulde drücken.",
"ingredients": [],
"timers": []
},
{
"id": "grillen",
"title": "Kräftig grillen",
"text": "Grill oder Grillpfanne vorheizen. Laibchen von beiden Seiten je etwa fünf bis sechs Minuten braten. Faschiertes vollständig durchgaren: im Kern mindestens 70 °C für zwei Minuten erreichen.",
"ingredients": [],
"timers": [
{
"id": "erste-seite",
"label": "Erste Seite",
"seconds": 360
},
{
"id": "zweite-seite",
"label": "Zweite Seite",
"seconds": 360
}
]
},
{
"id": "servieren",
"title": "Ab an den Tisch",
"text": "In {{brot}} mit {{ajvar}} servieren. Nach Wunsch frische Zwiebelringe dazu reichen.",
"ingredients": [
"brot",
"ajvar"
],
"timers": []
}
],
"tips": "Nicht während des Bratens flachdrücken – so bleibt die Pljeskavica saftig. Für Schärfe etwas Chiliflocken zur Masse geben.",
"storage": "Reste rasch abkühlen, abgedeckt im Kühlschrank aufbewahren und innerhalb von zwei Tagen verbrauchen. Vor dem Essen gründlich erhitzen.",
"nutrition": null,
"published": false
}
]
}
+167
View File
@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<hamham version="1">
<recipes>
<recipe>
<title>Serbische Pljeskavica</title>
<subtitle>Saftig gegrillt, mit Ajvar und frischen Zwiebeln</subtitle>
<description>Ein Klassiker vom Balkan: würzige Faschierte-Laibchen, außen kräftig geröstet und innen saftig. Dazu passen Lepinja, Ajvar und ein knackiger Salat.</description>
<image></image>
<images></images>
<categories>
<category>Für Gäste</category>
</categories>
<tags>
<tag>Grillen</tag>
<tag>Familienliebling</tag>
</tags>
<cuisine>Serbien</cuisine>
<difficulty>Einfach</difficulty>
<prepMinutes>20</prepMinutes>
<cookMinutes>12</cookMinutes>
<restMinutes>60</restMinutes>
<servings>4</servings>
<ingredients>
<ingredient>
<id>fleisch</id>
<name>Rinderfaschiertes</name>
<amount>800</amount>
<unit>g</unit>
<section>Pljeskavica</section>
<note></note>
<optional>false</optional>
</ingredient>
<ingredient>
<id>zwiebel</id>
<name>Zwiebel</name>
<amount>1</amount>
<unit>Stück</unit>
<section>Pljeskavica</section>
<note>sehr fein gewürfelt</note>
<optional>false</optional>
</ingredient>
<ingredient>
<id>paprika</id>
<name>Paprikapulver edelsüß</name>
<amount>2</amount>
<unit>TL</unit>
<section>Pljeskavica</section>
<note></note>
<optional>false</optional>
</ingredient>
<ingredient>
<id>salz</id>
<name>Salz</name>
<amount>10</amount>
<unit>g</unit>
<section>Pljeskavica</section>
<note></note>
<optional>false</optional>
</ingredient>
<ingredient>
<id>pfeffer</id>
<name>Schwarzer Pfeffer</name>
<amount></amount>
<unit></unit>
<section>Pljeskavica</section>
<note>nach Geschmack</note>
<optional>false</optional>
</ingredient>
<ingredient>
<id>wasser</id>
<name>Mineralwasser</name>
<amount>60</amount>
<unit>ml</unit>
<section>Pljeskavica</section>
<note></note>
<optional>false</optional>
</ingredient>
<ingredient>
<id>ajvar</id>
<name>Ajvar</name>
<amount>120</amount>
<unit>g</unit>
<section>Zum Servieren</section>
<note></note>
<optional>true</optional>
</ingredient>
<ingredient>
<id>brot</id>
<name>Lepinja</name>
<amount>4</amount>
<unit>Stück</unit>
<section>Zum Servieren</section>
<note></note>
<optional>false</optional>
</ingredient>
</ingredients>
<steps>
<step>
<id>mischen</id>
<title>Die Mischung vorbereiten</title>
<text>{{fleisch}}, {{zwiebel}}, {{paprika}}, {{salz}} und {{wasser}} gründlich verkneten. Mit {{pfeffer}} abschmecken.</text>
<ingredients>
<ref>fleisch</ref>
<ref>zwiebel</ref>
<ref>paprika</ref>
<ref>salz</ref>
<ref>wasser</ref>
<ref>pfeffer</ref>
</ingredients>
<timers></timers>
</step>
<step>
<id>ruhen</id>
<title>Geschmack entwickeln lassen</title>
<text>Die Masse zugedeckt mindestens eine Stunde im Kühlschrank ruhen lassen.</text>
<ingredients></ingredients>
<timers>
<timer>
<id>ruhezeit</id>
<label>Im Kühlschrank ruhen</label>
<seconds>3600</seconds>
</timer>
</timers>
</step>
<step>
<id>formen</id>
<title>Flache Laibchen formen</title>
<text>Mit angefeuchteten Händen je Portion ein etwa 1,5 cm dickes Laibchen formen. In die Mitte eine leichte Mulde drücken.</text>
<ingredients></ingredients>
<timers></timers>
</step>
<step>
<id>grillen</id>
<title>Kräftig grillen</title>
<text>Grill oder Grillpfanne vorheizen. Laibchen von beiden Seiten je etwa fünf bis sechs Minuten braten. Faschiertes vollständig durchgaren: im Kern mindestens 70 °C für zwei Minuten erreichen.</text>
<ingredients></ingredients>
<timers>
<timer>
<id>erste-seite</id>
<label>Erste Seite</label>
<seconds>360</seconds>
</timer>
<timer>
<id>zweite-seite</id>
<label>Zweite Seite</label>
<seconds>360</seconds>
</timer>
</timers>
</step>
<step>
<id>servieren</id>
<title>Ab an den Tisch</title>
<text>In {{brot}} mit {{ajvar}} servieren. Nach Wunsch frische Zwiebelringe dazu reichen.</text>
<ingredients>
<ref>brot</ref>
<ref>ajvar</ref>
</ingredients>
<timers></timers>
</step>
</steps>
<tips>Nicht während des Bratens flachdrücken – so bleibt die Pljeskavica saftig. Für Schärfe etwas Chiliflocken zur Masse geben.</tips>
<storage>Reste rasch abkühlen, abgedeckt im Kühlschrank aufbewahren und innerhalb von zwei Tagen verbrauchen. Vor dem Essen gründlich erhitzen.</storage>
<nutrition></nutrition>
<published>false</published>
</recipe>
</recipes>
</hamham>