Adds four new routers, three schema files, and one new model.
All new endpoints use new URL prefixes — no existing routes touched.
routers/menu.py
GET /api/menu/{site_slug} — public menu snapshot (no auth)
POST /api/menu/sync — upsert snapshot (site API key)
routers/orders.py
POST /api/orders/{site_slug} — customer places order (public)
GET /api/orders/status/{public_ref} — customer tracks order (public)
GET /api/orders/pending/{site_id} — local backend polls (site key)
POST /api/orders/{id}/synced — mark synced (site key)
PATCH /api/orders/{id}/status — update status with transition
validation (site key)
routers/manager_auth.py
POST /api/manager/register — create manager account (admin JWT)
POST /api/manager/login — returns manager JWT
POST /api/manager/refresh — refreshes manager JWT
Shared _get_current_manager dependency used by remote_dashboard
routers/remote_dashboard.py
GET /api/remote/sites — manager JWT
GET /api/remote/sites/{id}/snapshot — manager JWT
GET /api/remote/sites/{id}/orders — manager JWT
GET /api/remote/sites/{id}/orders/pending — manager JWT
PATCH /api/remote/sites/{id}/orders/{oid}/status — manager JWT
POST /api/remote/snapshot — site API key
(stats push)
models/stats_snapshot.py (NEW)
stats_snapshots table — one row per site, holds serialized
operational stats; created by create_all on startup
config.py / .env.example
MANAGER_JWT_SECRET and MANAGER_JWT_EXPIRE_HOURS (default 72h)
added as separate settings from the admin SECRET_KEY
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
64 lines
1.3 KiB
Python
64 lines
1.3 KiB
Python
from __future__ import annotations
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
from pydantic import BaseModel
|
|
|
|
|
|
class OrderItemIn(BaseModel):
|
|
product_id: int
|
|
name: str
|
|
quantity: int
|
|
unit_price: float
|
|
options: list = []
|
|
|
|
|
|
class OnlineOrderCreate(BaseModel):
|
|
order_type: str # "delivery" | "dine_in"
|
|
customer_name: str
|
|
customer_phone: Optional[str] = None
|
|
customer_address: Optional[str] = None
|
|
customer_notes: Optional[str] = None
|
|
items: list[OrderItemIn]
|
|
subtotal: float
|
|
delivery_fee: float = 0.0
|
|
total: float
|
|
|
|
|
|
class OnlineOrderCreated(BaseModel):
|
|
order_id: int
|
|
public_ref: str
|
|
status: str
|
|
|
|
|
|
class OrderStatusOut(BaseModel):
|
|
public_ref: str
|
|
status: str
|
|
rejection_reason: Optional[str] = None
|
|
|
|
|
|
class PendingOrderOut(BaseModel):
|
|
id: int
|
|
public_ref: str
|
|
order_type: str
|
|
customer_name: str
|
|
customer_phone: Optional[str] = None
|
|
customer_address: Optional[str] = None
|
|
customer_notes: Optional[str] = None
|
|
items_json: str
|
|
subtotal: float
|
|
delivery_fee: float
|
|
total: float
|
|
status: str
|
|
created_at: datetime
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class OrderSyncedRequest(BaseModel):
|
|
local_order_id: int
|
|
|
|
|
|
class OrderStatusUpdateRequest(BaseModel):
|
|
status: str
|
|
rejection_reason: Optional[str] = None
|