Fix 413 upload limit + add /api/kds/settings to kitchen backend
- nginx client_max_body_size raised to 800m (was 20m) to allow 577MB backup upload; proxy_read_timeout raised to 600s for long restore operations - Add api/kds_settings.py: GET+PATCH /api/kds/settings served by kitchen backend (kitchen Settings page configures KDS timers/GraphQL/course order; all config lives in kitchen_settings so kitchen owns these endpoints) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
14903e80a6
commit
e7e1fda9f6
3 changed files with 141 additions and 3 deletions
137
backend/api/kds_settings.py
Normal file
137
backend/api/kds_settings.py
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
"""
|
||||
KDS settings endpoints served by the kitchen backend.
|
||||
|
||||
The kitchen Settings page configures KDS (timers, GraphQL URL, course order, etc.)
|
||||
via /kitchen/api/kds/settings. All KDS config lives in kitchen_settings — so these
|
||||
endpoints belong here, not in the KDS backend.
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from auth import get_current_user, require_cap
|
||||
from database import get_db
|
||||
from models.settings import KitchenSettings
|
||||
from models.user import User
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class KDSSettingsResponse(BaseModel):
|
||||
kds_enabled: bool = False
|
||||
kds_graphql_url: Optional[str] = None
|
||||
kds_graphql_username: Optional[str] = None
|
||||
kds_graphql_password_set: bool = False
|
||||
kds_graphql_client_id: Optional[str] = None
|
||||
kds_poll_interval_seconds: int = 6000
|
||||
kds_timer_green_seconds: int = 300
|
||||
kds_timer_amber_seconds: int = 600
|
||||
kds_timer_red_seconds: int = 900
|
||||
kds_away_timer_green_seconds: int = 600
|
||||
kds_away_timer_amber_seconds: int = 900
|
||||
kds_away_timer_red_seconds: int = 1200
|
||||
kds_course_order: list = ["Starters", "Mains", "Desserts"]
|
||||
kds_show_completed_for_seconds: int = 30
|
||||
kds_bookings_refresh_seconds: int = 60
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class KDSSettingsUpdate(BaseModel):
|
||||
kds_enabled: Optional[bool] = None
|
||||
kds_graphql_url: Optional[str] = None
|
||||
kds_graphql_username: Optional[str] = None
|
||||
kds_graphql_password: Optional[str] = None
|
||||
kds_graphql_client_id: Optional[str] = None
|
||||
kds_poll_interval_seconds: Optional[int] = None
|
||||
kds_timer_green_seconds: Optional[int] = None
|
||||
kds_timer_amber_seconds: Optional[int] = None
|
||||
kds_timer_red_seconds: Optional[int] = None
|
||||
kds_away_timer_green_seconds: Optional[int] = None
|
||||
kds_away_timer_amber_seconds: Optional[int] = None
|
||||
kds_away_timer_red_seconds: Optional[int] = None
|
||||
kds_course_order: Optional[list] = None
|
||||
kds_show_completed_for_seconds: Optional[int] = None
|
||||
kds_bookings_refresh_seconds: Optional[int] = None
|
||||
|
||||
|
||||
def _normalize_course_config(course_order: list, s: KitchenSettings) -> list:
|
||||
result = []
|
||||
for entry in course_order:
|
||||
if isinstance(entry, str):
|
||||
result.append({
|
||||
"name": entry,
|
||||
"prep_green": s.kds_timer_green_seconds or 300,
|
||||
"prep_amber": s.kds_timer_amber_seconds or 600,
|
||||
"prep_red": s.kds_timer_red_seconds or 900,
|
||||
"away_green": s.kds_away_timer_green_seconds or 600,
|
||||
"away_amber": s.kds_away_timer_amber_seconds or 900,
|
||||
"away_red": s.kds_away_timer_red_seconds or 1200,
|
||||
})
|
||||
elif isinstance(entry, dict) and "name" in entry:
|
||||
result.append(entry)
|
||||
return result
|
||||
|
||||
|
||||
async def _get_settings(db: AsyncSession, kitchen_id: int):
|
||||
result = await db.execute(
|
||||
select(KitchenSettings).where(KitchenSettings.kitchen_id == kitchen_id)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
def _to_response(s: KitchenSettings) -> KDSSettingsResponse:
|
||||
raw = s.kds_course_order or ["Starters", "Mains", "Desserts"]
|
||||
return KDSSettingsResponse(
|
||||
kds_enabled=s.kds_enabled or False,
|
||||
kds_graphql_url=s.kds_graphql_url,
|
||||
kds_graphql_username=s.kds_graphql_username,
|
||||
kds_graphql_password_set=bool(s.kds_graphql_password),
|
||||
kds_graphql_client_id=s.kds_graphql_client_id,
|
||||
kds_poll_interval_seconds=s.kds_poll_interval_seconds or 6000,
|
||||
kds_timer_green_seconds=s.kds_timer_green_seconds or 300,
|
||||
kds_timer_amber_seconds=s.kds_timer_amber_seconds or 600,
|
||||
kds_timer_red_seconds=s.kds_timer_red_seconds or 900,
|
||||
kds_away_timer_green_seconds=s.kds_away_timer_green_seconds or 600,
|
||||
kds_away_timer_amber_seconds=s.kds_away_timer_amber_seconds or 900,
|
||||
kds_away_timer_red_seconds=s.kds_away_timer_red_seconds or 1200,
|
||||
kds_course_order=_normalize_course_config(raw, s),
|
||||
kds_show_completed_for_seconds=s.kds_show_completed_for_seconds or 30,
|
||||
kds_bookings_refresh_seconds=s.kds_bookings_refresh_seconds or 60,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/settings", response_model=KDSSettingsResponse)
|
||||
async def get_kds_settings(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
s = await _get_settings(db, current_user.kitchen_id)
|
||||
if not s:
|
||||
return KDSSettingsResponse()
|
||||
return _to_response(s)
|
||||
|
||||
|
||||
@router.patch("/settings", response_model=KDSSettingsResponse)
|
||||
async def update_kds_settings(
|
||||
update: KDSSettingsUpdate,
|
||||
current_user: User = Depends(require_cap("settings")),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
s = await _get_settings(db, current_user.kitchen_id)
|
||||
if not s:
|
||||
raise HTTPException(status_code=404, detail="Kitchen settings not found")
|
||||
|
||||
for field, value in update.model_dump(exclude_unset=True).items():
|
||||
if hasattr(s, field):
|
||||
setattr(s, field, value)
|
||||
|
||||
s.updated_at = datetime.utcnow()
|
||||
await db.commit()
|
||||
await db.refresh(s)
|
||||
return _to_response(s)
|
||||
|
|
@ -16,7 +16,7 @@ from api import (
|
|||
backup, search, resos, calendar_events, residents_table_chart, disputes,
|
||||
credit_notes, public, logbook, imap, support, budget, cover_overrides,
|
||||
purchase_orders, cost_distributions, ingredients, recipes, food_flags,
|
||||
event_orders, external, menus, reconciliation,
|
||||
event_orders, external, menus, reconciliation, kds_settings,
|
||||
)
|
||||
from api.internal import router as internal_router
|
||||
|
||||
|
|
@ -191,6 +191,7 @@ app.include_router(food_flags.router, prefix="/api/food-flags", tags=["Food Flag
|
|||
app.include_router(event_orders.router, prefix="/api/event-orders", tags=["Event Orders"])
|
||||
app.include_router(menus.router, prefix="/api/menus", tags=["Menus"])
|
||||
app.include_router(external.router, prefix="/api/external", tags=["External API"])
|
||||
app.include_router(kds_settings.router, prefix="/api/kds", tags=["KDS Settings"])
|
||||
app.include_router(internal_router)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue