{% extends "base.html" %}
{% block title %}Server API - Cook{% endblock %}
{% block content %}
<div class="max-w-5xl">
<h1 class="text-2xl font-bold mb-2">Server API</h1>
<p class="text-gray-600 dark:text-gray-400 mb-6">
HTTP endpoints for building integrations against this CookCLI server.
</p>
<div class="bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-gray-800 dark:to-gray-800 p-6 rounded-2xl border-2 border-blue-200 dark:border-gray-700 mb-8">
<h2 class="text-lg font-semibold mb-3 text-blue-900 dark:text-blue-200">Before you start</h2>
<div class="text-sm space-y-2 text-gray-700 dark:text-gray-300">
<div>
<span class="font-medium">Base URL:</span>
<code class="ml-2 px-2 py-0.5 rounded bg-white dark:bg-gray-900 font-mono">{{ base_url }}</code>
</div>
<div>
<span class="font-medium">Authentication:</span>
<span class="ml-2">None. Anyone who can reach the server can read and modify your recipes — think twice before using <code class="font-mono">--host</code> on an untrusted network.</span>
</div>
<div>
<span class="font-medium">CORS:</span>
<span class="ml-2">All origins are allowed, for the methods GET, POST, PUT and DELETE.</span>
</div>
<div>
<span class="font-medium">Request size limit:</span>
<span class="ml-2">1 MB.</span>
</div>
<div>
<span class="font-medium">Content type:</span>
<span class="ml-2">JSON in and out, except where noted — raw recipe text is <code class="font-mono">text/plain</code>.</span>
</div>
</div>
</div>
<div class="bg-gray-50 dark:bg-gray-800 p-6 rounded-2xl border border-gray-200 dark:border-gray-700 mb-8">
<h2 class="text-lg font-semibold mb-3">Errors</h2>
<p class="text-sm text-gray-700 dark:text-gray-300 mb-3">
Every failure returns the same shape, with the status code carrying the meaning:
</p>
<pre class="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs overflow-x-auto font-mono">{ "error": "Recipe not found: Nope.cook" }</pre>
<ul class="mt-3 text-sm space-y-1 text-gray-700 dark:text-gray-300">
<li><span class="font-mono font-medium">400</span> — malformed input: an invalid path, a bad query parameter, or a recipe that failed to parse.</li>
<li><span class="font-mono font-medium">404</span> — the recipe, menu, or pantry section does not exist, or no pantry file is configured.</li>
<li><span class="font-mono font-medium">500</span> — the server could not read or write a file.</li>
</ul>
</div>
<div class="bg-gray-50 dark:bg-gray-800 p-6 rounded-2xl border border-gray-200 dark:border-gray-700 mb-8">
<h2 class="text-lg font-semibold mb-3">Contents</h2>
<div class="flex flex-wrap gap-2">
{% for section in sections %}
<a href="#{{ section.id }}"
class="px-3 py-1.5 rounded-lg text-sm font-medium bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 hover:border-orange-400 hover:bg-orange-50 dark:hover:bg-gray-700 transition-colors">
{{ section.title }}
</a>
{% endfor %}
</div>
</div>
{% for section in sections %}
<section id="{{ section.id }}" class="mb-10 scroll-mt-24">
<h2 class="text-xl font-bold mb-2">{{ section.title }}</h2>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">{{ section.description|inline_code|safe }}</p>
<div class="space-y-4">
{% for endpoint in section.endpoints %}
<article class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-5">
<div class="flex flex-wrap items-center gap-2 mb-2">
<span class="px-2 py-0.5 rounded text-xs font-bold font-mono {{ endpoint.method_classes() }}">{{ endpoint.method }}</span>
<code class="font-mono text-sm break-all">{{ endpoint.path }}</code>
{% if let Some(feature) = endpoint.feature %}
<span class="px-2 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">
requires <span class="font-mono">{{ feature }}</span> build
</span>
{% endif %}
</div>
<p class="text-sm font-medium mb-1">{{ endpoint.summary|inline_code|safe }}</p>
{% if !endpoint.description.is_empty() %}
<p class="text-sm text-gray-600 dark:text-gray-400">{{ endpoint.description|inline_code|safe }}</p>
{% endif %}
{% if !endpoint.params.is_empty() %}
<div class="mt-4 overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="text-left text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700">
<th class="py-1.5 pr-4 font-medium">Name</th>
<th class="py-1.5 pr-4 font-medium">In</th>
<th class="py-1.5 pr-4 font-medium">Type</th>
<th class="py-1.5 font-medium">Description</th>
</tr>
</thead>
<tbody>
{% for p in endpoint.params %}
<tr class="border-b border-gray-100 dark:border-gray-700 align-top">
<td class="py-1.5 pr-4 font-mono whitespace-nowrap">
{{ p.name }}{% if p.required %}<span class="text-red-600 dark:text-red-400" title="required">*</span>{% endif %}
</td>
<td class="py-1.5 pr-4 text-gray-500 dark:text-gray-400">{{ p.kind }}</td>
<td class="py-1.5 pr-4 font-mono text-gray-500 dark:text-gray-400 whitespace-nowrap">{{ p.type_name }}</td>
<td class="py-1.5 text-gray-600 dark:text-gray-400">{{ p.description|inline_code|safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
<span class="text-red-600 dark:text-red-400">*</span> required
</p>
</div>
{% endif %}
{% if let Some(request) = endpoint.request_example %}
<div class="mt-4">
<h4 class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1">Request body</h4>
<pre class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs overflow-x-auto font-mono">{{ request }}</pre>
</div>
{% endif %}
{% if let Some(response) = endpoint.response_example %}
<div class="mt-4">
<h4 class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-1">Response</h4>
<pre class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-3 text-xs overflow-x-auto font-mono">{{ response }}</pre>
</div>
{% endif %}
</article>
{% endfor %}
</div>
</section>
{% endfor %}
</div>
{% endblock %}