Fix /recipes redirect loop by merging duplicate routes
Removed duplicate /recipes route that was causing infinite redirect. Now single route handles both HTML and JSON responses directly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
139
server.py
139
server.py
@@ -1055,19 +1055,76 @@ async def list_recipes_api(request: Request, page: int = 1, limit: int = 20):
|
||||
current_user = get_user_from_cookie(request)
|
||||
|
||||
all_recipes = list_all_recipes()
|
||||
total = len(all_recipes)
|
||||
|
||||
# Pagination
|
||||
if wants_html(request):
|
||||
# HTML response
|
||||
if not current_user:
|
||||
return HTMLResponse(render_page(
|
||||
"Recipes",
|
||||
'<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login</a> to see recipes.</p>',
|
||||
None,
|
||||
active_tab="recipes"
|
||||
))
|
||||
|
||||
# Filter to user's recipes
|
||||
actor_id = f"@{current_user}@{L2_DOMAIN}"
|
||||
user_recipes = [c for c in all_recipes if c.uploader in (current_user, actor_id)]
|
||||
total = len(user_recipes)
|
||||
|
||||
if not user_recipes:
|
||||
content = '''
|
||||
<h2 class="text-xl font-semibold text-white mb-6">Recipes (0)</h2>
|
||||
<p class="text-gray-400 py-8 text-center">No recipes yet. Upload a recipe YAML file to get started.</p>
|
||||
'''
|
||||
return HTMLResponse(render_page("Recipes", content, current_user, active_tab="recipes"))
|
||||
|
||||
html_parts = []
|
||||
for recipe in user_recipes:
|
||||
var_count = len(recipe.variable_inputs)
|
||||
fixed_count = len(recipe.fixed_inputs)
|
||||
input_info = []
|
||||
if var_count:
|
||||
input_info.append(f"{var_count} variable")
|
||||
if fixed_count:
|
||||
input_info.append(f"{fixed_count} fixed")
|
||||
inputs_str = ", ".join(input_info) if input_info else "no inputs"
|
||||
|
||||
html_parts.append(f'''
|
||||
<a href="/recipe/{recipe.recipe_id}" class="block">
|
||||
<div class="bg-dark-700 rounded-lg p-4 hover:bg-dark-600 transition-colors">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3 mb-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-3 py-1 bg-purple-600 text-white text-sm font-medium rounded-full">{recipe.name}</span>
|
||||
<span class="text-gray-400 text-xs">v{recipe.version}</span>
|
||||
</div>
|
||||
<span class="text-xs text-gray-400">{inputs_str}</span>
|
||||
</div>
|
||||
<div class="text-sm text-gray-400 mb-2">
|
||||
{recipe.description or "No description"}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 font-mono truncate">
|
||||
{recipe.recipe_id[:24]}...
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
''')
|
||||
|
||||
content = f'''
|
||||
<h2 class="text-xl font-semibold text-white mb-6">Recipes ({total})</h2>
|
||||
<div class="space-y-4">
|
||||
{''.join(html_parts)}
|
||||
</div>
|
||||
'''
|
||||
|
||||
return HTMLResponse(render_page("Recipes", content, current_user, active_tab="recipes"))
|
||||
|
||||
# JSON response for APIs
|
||||
total = len(all_recipes)
|
||||
start = (page - 1) * limit
|
||||
end = start + limit
|
||||
recipes_page = all_recipes[start:end]
|
||||
has_more = end < total
|
||||
|
||||
if wants_html(request):
|
||||
# HTML response - redirect to /recipes page with proper UI
|
||||
return RedirectResponse(f"/recipes?page={page}")
|
||||
|
||||
# JSON response for APIs
|
||||
return {
|
||||
"recipes": [c.model_dump() for c in recipes_page],
|
||||
"pagination": {
|
||||
@@ -1222,74 +1279,6 @@ def build_dag_from_recipe(yaml_config: dict, user_inputs: dict[str, str], recipe
|
||||
|
||||
# ============ Recipe UI Pages ============
|
||||
|
||||
@app.get("/recipes", response_class=HTMLResponse)
|
||||
async def recipes_page(request: Request, page: int = 1):
|
||||
"""Recipes list page (HTML)."""
|
||||
current_user = get_user_from_cookie(request)
|
||||
|
||||
if not current_user:
|
||||
return HTMLResponse(render_page(
|
||||
"Recipes",
|
||||
'<p class="text-gray-400 py-8 text-center"><a href="/login" class="text-blue-400 hover:text-blue-300">Login</a> to see recipes.</p>',
|
||||
None,
|
||||
active_tab="recipes"
|
||||
))
|
||||
|
||||
all_recipes = list_all_recipes()
|
||||
|
||||
# Filter to user's configs
|
||||
actor_id = f"@{current_user}@{L2_DOMAIN}"
|
||||
user_recipes = [c for c in all_recipes if c.uploader in (current_user, actor_id)]
|
||||
total = len(user_recipes)
|
||||
|
||||
if not user_recipes:
|
||||
content = '''
|
||||
<h2 class="text-xl font-semibold text-white mb-6">Recipes (0)</h2>
|
||||
<p class="text-gray-400 py-8 text-center">No recipes yet. Upload a recipe YAML file to get started.</p>
|
||||
'''
|
||||
return HTMLResponse(render_page("Recipes", content, current_user, active_tab="recipes"))
|
||||
|
||||
html_parts = []
|
||||
for recipe in user_recipes:
|
||||
var_count = len(recipe.variable_inputs)
|
||||
fixed_count = len(recipe.fixed_inputs)
|
||||
input_info = []
|
||||
if var_count:
|
||||
input_info.append(f"{var_count} variable")
|
||||
if fixed_count:
|
||||
input_info.append(f"{fixed_count} fixed")
|
||||
inputs_str = ", ".join(input_info) if input_info else "no inputs"
|
||||
|
||||
html_parts.append(f'''
|
||||
<a href="/recipe/{recipe.recipe_id}" class="block">
|
||||
<div class="bg-dark-700 rounded-lg p-4 hover:bg-dark-600 transition-colors">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3 mb-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-3 py-1 bg-purple-600 text-white text-sm font-medium rounded-full">{recipe.name}</span>
|
||||
<span class="text-gray-400 text-xs">v{recipe.version}</span>
|
||||
</div>
|
||||
<span class="text-xs text-gray-400">{inputs_str}</span>
|
||||
</div>
|
||||
<div class="text-sm text-gray-400 mb-2">
|
||||
{recipe.description or "No description"}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 font-mono truncate">
|
||||
{recipe.recipe_id[:24]}...
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
''')
|
||||
|
||||
content = f'''
|
||||
<h2 class="text-xl font-semibold text-white mb-6">Recipes ({total})</h2>
|
||||
<div class="space-y-4">
|
||||
{''.join(html_parts)}
|
||||
</div>
|
||||
'''
|
||||
|
||||
return HTMLResponse(render_page("Recipes", content, current_user, active_tab="recipes"))
|
||||
|
||||
|
||||
@app.get("/recipe/{recipe_id}", response_class=HTMLResponse)
|
||||
async def recipe_detail_page(recipe_id: str, request: Request):
|
||||
"""Recipe detail page with run form."""
|
||||
|
||||
Reference in New Issue
Block a user