Public Access
fix: move orchestrate endpoints out of admin router
- Create backend/app/api/orchestrate.py — new router for workflow steps
(scrape, generate, email, reminder, deadline, finalize) without admin auth.
- Remove orchestrate endpoints from backend/app/api/admin.py.
- Register orchestrate router in main.py under /api/orchestrate.
- Update frontend api/index.ts to call /orchestrate/{step} instead of
/admin/orchestrate/{step}.
This lets family members trigger vote emails without an admin bearer token.
This commit is contained in:
@@ -6,9 +6,6 @@ from app.security import require_admin
|
|||||||
from app.services.scraper_service import ScraperService, enqueue_scrape
|
from app.services.scraper_service import ScraperService, enqueue_scrape
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from datetime import date as DateType
|
|
||||||
from app.services.orchestrator import run_step, run_week
|
|
||||||
from app.models import WeeklyRun
|
|
||||||
|
|
||||||
router = APIRouter(dependencies=[Depends(require_admin)])
|
router = APIRouter(dependencies=[Depends(require_admin)])
|
||||||
|
|
||||||
@@ -183,63 +180,3 @@ def get_stats(db: Session = Depends(get_db)):
|
|||||||
"ingredients": ingredient_count,
|
"ingredients": ingredient_count,
|
||||||
"meal_plans": plan_count
|
"meal_plans": plan_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_VALID_STEPS = {"scrape", "generate", "email", "reminder", "deadline", "finalize"}
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/orchestrate/status")
|
|
||||||
def orchestrate_status(limit: int = 10, db: Session = Depends(get_db)):
|
|
||||||
runs = (
|
|
||||||
db.query(WeeklyRun)
|
|
||||||
.order_by(WeeklyRun.week_start_date.desc())
|
|
||||||
.limit(limit)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
"runs": [
|
|
||||||
{
|
|
||||||
"id": str(r.id),
|
|
||||||
"family_id": str(r.family_id),
|
|
||||||
"week_start_date": r.week_start_date.isoformat(),
|
|
||||||
"status": r.status,
|
|
||||||
"scraped_at": r.scraped_at.isoformat() if r.scraped_at else None,
|
|
||||||
"generated_at": r.generated_at.isoformat() if r.generated_at else None,
|
|
||||||
"emailed_at": r.emailed_at.isoformat() if r.emailed_at else None,
|
|
||||||
"deadline_passed_at": r.deadline_passed_at.isoformat()
|
|
||||||
if r.deadline_passed_at
|
|
||||||
else None,
|
|
||||||
"finalized_at": r.finalized_at.isoformat() if r.finalized_at else None,
|
|
||||||
"used_stale_data": r.used_stale_data,
|
|
||||||
"error_step": r.error_step,
|
|
||||||
"error_message": r.error_message,
|
|
||||||
}
|
|
||||||
for r in runs
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/orchestrate/run-week", status_code=202)
|
|
||||||
def orchestrate_run_week(
|
|
||||||
background_tasks: BackgroundTasks,
|
|
||||||
week_start: Optional[str] = None,
|
|
||||||
):
|
|
||||||
week = DateType.fromisoformat(week_start) if week_start else None
|
|
||||||
background_tasks.add_task(run_week, week)
|
|
||||||
return {"status": "queued", "week_start": str(week or "current")}
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/orchestrate/{step}", status_code=202)
|
|
||||||
def orchestrate_step(
|
|
||||||
step: str,
|
|
||||||
background_tasks: BackgroundTasks,
|
|
||||||
week_start: Optional[str] = None,
|
|
||||||
):
|
|
||||||
if step not in _VALID_STEPS:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400,
|
|
||||||
detail=f"Unknown step {step!r}. Valid: {sorted(_VALID_STEPS)}",
|
|
||||||
)
|
|
||||||
week = DateType.fromisoformat(week_start) if week_start else None
|
|
||||||
background_tasks.add_task(run_step, step, week)
|
|
||||||
return {"status": "queued", "step": step, "week_start": str(week or "current")}
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from app.database import get_db
|
||||||
|
from app.models import WeeklyRun
|
||||||
|
from app.services.orchestrator import run_step, run_week
|
||||||
|
from typing import Optional
|
||||||
|
from datetime import date as DateType
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
_VALID_STEPS = {"scrape", "generate", "email", "reminder", "deadline", "finalize"}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/status")
|
||||||
|
def orchestrate_status(limit: int = 10, db: Session = Depends(get_db)):
|
||||||
|
runs = (
|
||||||
|
db.query(WeeklyRun)
|
||||||
|
.order_by(WeeklyRun.week_start_date.desc())
|
||||||
|
.limit(limit)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"runs": [
|
||||||
|
{
|
||||||
|
"id": str(r.id),
|
||||||
|
"week_start_date": str(r.week_start_date),
|
||||||
|
"scraped_at": r.scraped_at.isoformat() if r.scraped_at else None,
|
||||||
|
"generated_at": r.generated_at.isoformat() if r.generated_at else None,
|
||||||
|
"emailed_at": r.emailed_at.isoformat() if r.emailed_at else None,
|
||||||
|
"reminded_at": r.reminded_at.isoformat() if r.reminded_at else None,
|
||||||
|
"deadlined_at": r.deadlined_at.isoformat() if r.deadlined_at else None,
|
||||||
|
"finalized_at": r.finalized_at.isoformat() if r.finalized_at else None,
|
||||||
|
"error_message": r.error_message,
|
||||||
|
}
|
||||||
|
for r in runs
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/run-week", status_code=202)
|
||||||
|
def orchestrate_run_week(
|
||||||
|
background_tasks: BackgroundTasks,
|
||||||
|
week_start: Optional[str] = None,
|
||||||
|
):
|
||||||
|
week = DateType.fromisoformat(week_start) if week_start else None
|
||||||
|
background_tasks.add_task(run_week, week)
|
||||||
|
return {"status": "queued", "week_start": str(week or "current")}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{step}", status_code=202)
|
||||||
|
def orchestrate_step(
|
||||||
|
step: str,
|
||||||
|
background_tasks: BackgroundTasks,
|
||||||
|
week_start: Optional[str] = None,
|
||||||
|
):
|
||||||
|
if step not in _VALID_STEPS:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail=f"Unknown step {step!r}. Valid: {sorted(_VALID_STEPS)}",
|
||||||
|
)
|
||||||
|
week = DateType.fromisoformat(week_start) if week_start else None
|
||||||
|
background_tasks.add_task(run_step, step, week)
|
||||||
|
return {"status": "queued", "step": step, "week_start": str(week or "current")}
|
||||||
@@ -35,6 +35,7 @@ from app.api import recipes as recipes_api
|
|||||||
from app.api import never_suggest as never_suggest_api
|
from app.api import never_suggest as never_suggest_api
|
||||||
from app.api import meal_plans as meal_plans_api
|
from app.api import meal_plans as meal_plans_api
|
||||||
from app.api import feedback as feedback_api
|
from app.api import feedback as feedback_api
|
||||||
|
from app.api import orchestrate as orchestrate_api
|
||||||
|
|
||||||
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
|
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
|
||||||
app.include_router(profile.router, prefix="/api/profile", tags=["profile"])
|
app.include_router(profile.router, prefix="/api/profile", tags=["profile"])
|
||||||
@@ -42,6 +43,7 @@ app.include_router(meals.router, prefix="/api/meals", tags=["meals"])
|
|||||||
app.include_router(shopping_list.router, prefix="/api/shopping-list", tags=["shopping-list"])
|
app.include_router(shopping_list.router, prefix="/api/shopping-list", tags=["shopping-list"])
|
||||||
app.include_router(pantry.router, prefix="/api/pantry", tags=["pantry"])
|
app.include_router(pantry.router, prefix="/api/pantry", tags=["pantry"])
|
||||||
app.include_router(admin.router, prefix="/api/admin", tags=["admin"])
|
app.include_router(admin.router, prefix="/api/admin", tags=["admin"])
|
||||||
|
app.include_router(orchestrate_api.router, prefix="/api/orchestrate", tags=["orchestrate"])
|
||||||
app.include_router(ingredients_api.public_router)
|
app.include_router(ingredients_api.public_router)
|
||||||
app.include_router(ingredients_api.admin_router)
|
app.include_router(ingredients_api.admin_router)
|
||||||
app.include_router(ingredients_api._match_admin_router)
|
app.include_router(ingredients_api._match_admin_router)
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const mealPlannerApi = {
|
|||||||
getMealPlans: (params?: any) => api.get('/admin/meal-plans', { params }),
|
getMealPlans: (params?: any) => api.get('/admin/meal-plans', { params }),
|
||||||
getStats: () => api.get('/admin/stats'),
|
getStats: () => api.get('/admin/stats'),
|
||||||
testEmail: (email: string) => api.post('/admin/test-email', null, { params: { email } }),
|
testEmail: (email: string) => api.post('/admin/test-email', null, { params: { email } }),
|
||||||
triggerOrchestrate: (step: string, weekStart?: string) => api.post(`/admin/orchestrate/${step}`, null, { params: { week_start: weekStart } }),
|
triggerOrchestrate: (step: string, weekStart?: string) => api.post(`/orchestrate/${step}`, null, { params: { week_start: weekStart } }),
|
||||||
},
|
},
|
||||||
|
|
||||||
feedback: {
|
feedback: {
|
||||||
|
|||||||
Reference in New Issue
Block a user