Add admin panel, cart, checkout, and digital key delivery
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,414 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from decimal import Decimal, InvalidOperation
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, Request
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from sqlalchemy import delete, func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.auth import is_admin, login_admin, logout_admin
|
||||
from app.cart import available_stock
|
||||
from app.config import get_settings
|
||||
from app.database import get_db
|
||||
from app.models import Category, Order, Product, ProductKey
|
||||
|
||||
router = APIRouter(prefix="/admin")
|
||||
settings = get_settings()
|
||||
|
||||
|
||||
def setup(templates: Jinja2Templates) -> None:
|
||||
router.templates = templates # type: ignore[attr-defined]
|
||||
|
||||
|
||||
def admin_ctx(request: Request, **kwargs):
|
||||
data = {
|
||||
"request": request,
|
||||
"app_name": settings.app_name,
|
||||
"admin_user": request.session.get("admin_user", "admin"),
|
||||
}
|
||||
data.update(kwargs)
|
||||
return data
|
||||
|
||||
|
||||
def guard(request: Request):
|
||||
if not is_admin(request):
|
||||
return RedirectResponse("/admin/login", status_code=303)
|
||||
return None
|
||||
|
||||
|
||||
@router.get("/login", response_class=HTMLResponse)
|
||||
def admin_login_page(request: Request):
|
||||
if is_admin(request):
|
||||
return RedirectResponse("/admin", status_code=303)
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/login.html",
|
||||
admin_ctx(request, error=None),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/login", response_class=HTMLResponse)
|
||||
def admin_login_submit(
|
||||
request: Request,
|
||||
username: str = Form(...),
|
||||
password: str = Form(...),
|
||||
):
|
||||
if login_admin(request, username.strip(), password):
|
||||
return RedirectResponse("/admin", status_code=303)
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/login.html",
|
||||
admin_ctx(request, error="Неверный логин или пароль"),
|
||||
status_code=401,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/logout")
|
||||
def admin_logout(request: Request):
|
||||
logout_admin(request)
|
||||
return RedirectResponse("/admin/login", status_code=303)
|
||||
|
||||
|
||||
@router.get("", response_class=HTMLResponse)
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
def dashboard(request: Request, db: Session = Depends(get_db)):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
|
||||
products_count = db.scalar(select(func.count()).select_from(Product)) or 0
|
||||
keys_free = (
|
||||
db.scalar(
|
||||
select(func.count())
|
||||
.select_from(ProductKey)
|
||||
.where(ProductKey.is_sold.is_(False))
|
||||
)
|
||||
or 0
|
||||
)
|
||||
orders_count = db.scalar(select(func.count()).select_from(Order)) or 0
|
||||
revenue = (
|
||||
db.scalar(
|
||||
select(func.coalesce(func.sum(Order.total), 0)).where(
|
||||
Order.status == "delivered"
|
||||
)
|
||||
)
|
||||
or 0
|
||||
)
|
||||
recent_orders = db.scalars(select(Order).order_by(Order.id.desc()).limit(8)).all()
|
||||
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/dashboard.html",
|
||||
admin_ctx(
|
||||
request,
|
||||
products_count=products_count,
|
||||
keys_free=keys_free,
|
||||
orders_count=orders_count,
|
||||
revenue=revenue,
|
||||
recent_orders=recent_orders,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/products", response_class=HTMLResponse)
|
||||
def products_list(request: Request, db: Session = Depends(get_db)):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
products = db.scalars(select(Product).order_by(Product.id.desc())).all()
|
||||
stock_map = {p.id: available_stock(db, p.id) for p in products}
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/products.html",
|
||||
admin_ctx(request, products=products, stock_map=stock_map),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/products/new", response_class=HTMLResponse)
|
||||
def product_new(request: Request, db: Session = Depends(get_db)):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
categories = db.scalars(select(Category).order_by(Category.sort_order)).all()
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/product_form.html",
|
||||
admin_ctx(
|
||||
request,
|
||||
product=None,
|
||||
categories=categories,
|
||||
error=None,
|
||||
form={},
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/products/new", response_class=HTMLResponse)
|
||||
def product_create(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
title: str = Form(...),
|
||||
slug: str = Form(...),
|
||||
short_description: str = Form(...),
|
||||
description: str = Form(""),
|
||||
price: str = Form(...),
|
||||
category_slug: str = Form(...),
|
||||
image_gradient: str = Form("mint"),
|
||||
delivery_note: str = Form("Мгновенная выдача"),
|
||||
is_featured: str | None = Form(None),
|
||||
is_active: str | None = Form(None),
|
||||
):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
categories = db.scalars(select(Category).order_by(Category.sort_order)).all()
|
||||
form = {
|
||||
"title": title,
|
||||
"slug": slug,
|
||||
"short_description": short_description,
|
||||
"description": description,
|
||||
"price": price,
|
||||
"category_slug": category_slug,
|
||||
"image_gradient": image_gradient,
|
||||
"delivery_note": delivery_note,
|
||||
"is_featured": bool(is_featured),
|
||||
"is_active": bool(is_active) if is_active is not None else True,
|
||||
}
|
||||
try:
|
||||
price_val = Decimal(price.replace(",", ".").replace(" ", ""))
|
||||
except (InvalidOperation, AttributeError):
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/product_form.html",
|
||||
admin_ctx(
|
||||
request,
|
||||
product=None,
|
||||
categories=categories,
|
||||
error="Некорректная цена",
|
||||
form=form,
|
||||
),
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
slug_clean = slug.strip().lower().replace(" ", "-")
|
||||
exists = db.scalar(select(Product.id).where(Product.slug == slug_clean))
|
||||
if exists:
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/product_form.html",
|
||||
admin_ctx(
|
||||
request,
|
||||
product=None,
|
||||
categories=categories,
|
||||
error="Slug уже занят",
|
||||
form=form,
|
||||
),
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
product = Product(
|
||||
title=title.strip(),
|
||||
slug=slug_clean,
|
||||
short_description=short_description.strip()[:300],
|
||||
description=description.strip(),
|
||||
price=price_val,
|
||||
category_slug=category_slug,
|
||||
image_gradient=image_gradient,
|
||||
delivery_note=delivery_note.strip()[:200],
|
||||
is_featured=bool(is_featured),
|
||||
is_active=bool(is_active),
|
||||
)
|
||||
db.add(product)
|
||||
db.commit()
|
||||
return RedirectResponse(f"/admin/products/{product.id}/keys", status_code=303)
|
||||
|
||||
|
||||
@router.get("/products/{product_id}/edit", response_class=HTMLResponse)
|
||||
def product_edit(product_id: int, request: Request, db: Session = Depends(get_db)):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
product = db.get(Product, product_id)
|
||||
if not product:
|
||||
return RedirectResponse("/admin/products", status_code=303)
|
||||
categories = db.scalars(select(Category).order_by(Category.sort_order)).all()
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/product_form.html",
|
||||
admin_ctx(
|
||||
request,
|
||||
product=product,
|
||||
categories=categories,
|
||||
error=None,
|
||||
form={},
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/products/{product_id}/edit", response_class=HTMLResponse)
|
||||
def product_update(
|
||||
product_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
title: str = Form(...),
|
||||
slug: str = Form(...),
|
||||
short_description: str = Form(...),
|
||||
description: str = Form(""),
|
||||
price: str = Form(...),
|
||||
category_slug: str = Form(...),
|
||||
image_gradient: str = Form("mint"),
|
||||
delivery_note: str = Form("Мгновенная выдача"),
|
||||
is_featured: str | None = Form(None),
|
||||
is_active: str | None = Form(None),
|
||||
):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
product = db.get(Product, product_id)
|
||||
if not product:
|
||||
return RedirectResponse("/admin/products", status_code=303)
|
||||
categories = db.scalars(select(Category).order_by(Category.sort_order)).all()
|
||||
|
||||
try:
|
||||
price_val = Decimal(price.replace(",", ".").replace(" ", ""))
|
||||
except (InvalidOperation, AttributeError):
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/product_form.html",
|
||||
admin_ctx(
|
||||
request,
|
||||
product=product,
|
||||
categories=categories,
|
||||
error="Некорректная цена",
|
||||
form={},
|
||||
),
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
slug_clean = slug.strip().lower().replace(" ", "-")
|
||||
clash = db.scalar(
|
||||
select(Product.id).where(Product.slug == slug_clean, Product.id != product.id)
|
||||
)
|
||||
if clash:
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/product_form.html",
|
||||
admin_ctx(
|
||||
request,
|
||||
product=product,
|
||||
categories=categories,
|
||||
error="Slug уже занят",
|
||||
form={},
|
||||
),
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
product.title = title.strip()
|
||||
product.slug = slug_clean
|
||||
product.short_description = short_description.strip()[:300]
|
||||
product.description = description.strip()
|
||||
product.price = price_val
|
||||
product.category_slug = category_slug
|
||||
product.image_gradient = image_gradient
|
||||
product.delivery_note = delivery_note.strip()[:200]
|
||||
product.is_featured = bool(is_featured)
|
||||
product.is_active = bool(is_active)
|
||||
db.commit()
|
||||
return RedirectResponse("/admin/products", status_code=303)
|
||||
|
||||
|
||||
@router.post("/products/{product_id}/delete")
|
||||
def product_delete(product_id: int, request: Request, db: Session = Depends(get_db)):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
product = db.get(Product, product_id)
|
||||
if product:
|
||||
db.execute(delete(ProductKey).where(ProductKey.product_id == product_id))
|
||||
db.delete(product)
|
||||
db.commit()
|
||||
return RedirectResponse("/admin/products", status_code=303)
|
||||
|
||||
|
||||
@router.get("/products/{product_id}/keys", response_class=HTMLResponse)
|
||||
def product_keys(product_id: int, request: Request, db: Session = Depends(get_db)):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
product = db.get(Product, product_id)
|
||||
if not product:
|
||||
return RedirectResponse("/admin/products", status_code=303)
|
||||
keys = db.scalars(
|
||||
select(ProductKey)
|
||||
.where(ProductKey.product_id == product_id)
|
||||
.order_by(ProductKey.is_sold, ProductKey.id.desc())
|
||||
).all()
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/keys.html",
|
||||
admin_ctx(
|
||||
request,
|
||||
product=product,
|
||||
keys=keys,
|
||||
stock=available_stock(db, product_id),
|
||||
message=None,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/products/{product_id}/keys", response_class=HTMLResponse)
|
||||
def product_keys_add(
|
||||
product_id: int,
|
||||
request: Request,
|
||||
codes: str = Form(...),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
product = db.get(Product, product_id)
|
||||
if not product:
|
||||
return RedirectResponse("/admin/products", status_code=303)
|
||||
|
||||
lines = [line.strip() for line in codes.splitlines() if line.strip()]
|
||||
added = 0
|
||||
for code in lines:
|
||||
db.add(ProductKey(product_id=product_id, code=code))
|
||||
added += 1
|
||||
db.commit()
|
||||
|
||||
keys = db.scalars(
|
||||
select(ProductKey)
|
||||
.where(ProductKey.product_id == product_id)
|
||||
.order_by(ProductKey.is_sold, ProductKey.id.desc())
|
||||
).all()
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/keys.html",
|
||||
admin_ctx(
|
||||
request,
|
||||
product=product,
|
||||
keys=keys,
|
||||
stock=available_stock(db, product_id),
|
||||
message=f"Добавлено ключей: {added}",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/orders", response_class=HTMLResponse)
|
||||
def orders_list(request: Request, db: Session = Depends(get_db)):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
orders = db.scalars(select(Order).order_by(Order.id.desc()).limit(100)).all()
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/orders.html",
|
||||
admin_ctx(request, orders=orders),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/orders/{order_id}", response_class=HTMLResponse)
|
||||
def order_detail(order_id: int, request: Request, db: Session = Depends(get_db)):
|
||||
if (redir := guard(request)):
|
||||
return redir
|
||||
order = db.get(Order, order_id)
|
||||
if not order:
|
||||
return RedirectResponse("/admin/orders", status_code=303)
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"admin/order_detail.html",
|
||||
admin_ctx(request, order=order),
|
||||
)
|
||||
@@ -0,0 +1,260 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, Request
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.cart import (
|
||||
available_stock,
|
||||
build_cart_items,
|
||||
cart_count,
|
||||
get_cart_map,
|
||||
save_cart,
|
||||
)
|
||||
from app.config import get_settings
|
||||
from app.database import get_db
|
||||
from app.models import Category, Order, Product
|
||||
from app.orders import OrderError, create_order_from_cart, pay_and_deliver
|
||||
|
||||
router = APIRouter()
|
||||
settings = get_settings()
|
||||
|
||||
|
||||
def setup(templates: Jinja2Templates) -> None:
|
||||
router.templates = templates # type: ignore[attr-defined]
|
||||
|
||||
|
||||
def ctx(request: Request, **kwargs):
|
||||
data = {
|
||||
"request": request,
|
||||
"app_name": settings.app_name,
|
||||
"cart_count": cart_count(request),
|
||||
}
|
||||
data.update(kwargs)
|
||||
return data
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
def home(request: Request, db: Session = Depends(get_db)):
|
||||
categories = db.scalars(
|
||||
select(Category).order_by(Category.sort_order, Category.name)
|
||||
).all()
|
||||
featured = db.scalars(
|
||||
select(Product)
|
||||
.where(Product.is_active.is_(True), Product.is_featured.is_(True))
|
||||
.order_by(Product.id)
|
||||
.limit(4)
|
||||
).all()
|
||||
products = db.scalars(
|
||||
select(Product)
|
||||
.where(Product.is_active.is_(True))
|
||||
.order_by(Product.is_featured.desc(), Product.id)
|
||||
.limit(12)
|
||||
).all()
|
||||
stock_map = {
|
||||
p.id: available_stock(db, p.id) for p in [*featured, *products]
|
||||
}
|
||||
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"index.html",
|
||||
ctx(
|
||||
request,
|
||||
categories=categories,
|
||||
featured=featured,
|
||||
products=products,
|
||||
stock_map=stock_map,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/product/{slug}", response_class=HTMLResponse)
|
||||
def product_page(slug: str, request: Request, db: Session = Depends(get_db)):
|
||||
product = db.scalar(
|
||||
select(Product).where(Product.slug == slug, Product.is_active.is_(True))
|
||||
)
|
||||
if not product:
|
||||
return RedirectResponse("/", status_code=303)
|
||||
stock = available_stock(db, product.id)
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"product.html",
|
||||
ctx(request, product=product, stock=stock),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/cart/add/{product_id}")
|
||||
def cart_add(
|
||||
product_id: int,
|
||||
request: Request,
|
||||
quantity: int = Form(1),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
product = db.scalar(
|
||||
select(Product).where(Product.id == product_id, Product.is_active.is_(True))
|
||||
)
|
||||
if not product:
|
||||
return RedirectResponse("/", status_code=303)
|
||||
|
||||
qty = max(1, min(int(quantity or 1), 10))
|
||||
stock = available_stock(db, product.id)
|
||||
cart = get_cart_map(request)
|
||||
current = cart.get(str(product_id), 0)
|
||||
cart[str(product_id)] = min(current + qty, max(stock, 0))
|
||||
if cart[str(product_id)] <= 0:
|
||||
cart.pop(str(product_id), None)
|
||||
save_cart(request, cart)
|
||||
return RedirectResponse("/cart", status_code=303)
|
||||
|
||||
|
||||
@router.get("/cart", response_class=HTMLResponse)
|
||||
def cart_page(request: Request, db: Session = Depends(get_db)):
|
||||
items, total = build_cart_items(db, request)
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"cart.html",
|
||||
ctx(request, items=items, total=total),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/cart/update")
|
||||
def cart_update(
|
||||
request: Request,
|
||||
product_id: int = Form(...),
|
||||
quantity: int = Form(...),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
cart = get_cart_map(request)
|
||||
pid = str(product_id)
|
||||
qty = int(quantity)
|
||||
if qty <= 0:
|
||||
cart.pop(pid, None)
|
||||
else:
|
||||
stock = available_stock(db, product_id)
|
||||
cart[pid] = min(qty, max(stock, 0))
|
||||
if cart[pid] <= 0:
|
||||
cart.pop(pid, None)
|
||||
save_cart(request, cart)
|
||||
return RedirectResponse("/cart", status_code=303)
|
||||
|
||||
|
||||
@router.post("/cart/remove/{product_id}")
|
||||
def cart_remove(product_id: int, request: Request):
|
||||
cart = get_cart_map(request)
|
||||
cart.pop(str(product_id), None)
|
||||
save_cart(request, cart)
|
||||
return RedirectResponse("/cart", status_code=303)
|
||||
|
||||
|
||||
@router.get("/checkout", response_class=HTMLResponse)
|
||||
def checkout_page(request: Request, db: Session = Depends(get_db)):
|
||||
items, total = build_cart_items(db, request)
|
||||
if not items:
|
||||
return RedirectResponse("/cart", status_code=303)
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"checkout.html",
|
||||
ctx(request, items=items, total=total, error=None),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/checkout", response_class=HTMLResponse)
|
||||
def checkout_submit(
|
||||
request: Request,
|
||||
email: str = Form(...),
|
||||
note: str = Form(""),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
items, total = build_cart_items(db, request)
|
||||
if not items:
|
||||
return RedirectResponse("/cart", status_code=303)
|
||||
|
||||
cart = get_cart_map(request)
|
||||
try:
|
||||
order = create_order_from_cart(
|
||||
db, email=email, cart=cart, customer_note=note
|
||||
)
|
||||
order = pay_and_deliver(db, order)
|
||||
save_cart(request, {})
|
||||
return RedirectResponse(f"/order/{order.public_id}?email={order.email}", status_code=303)
|
||||
except OrderError as exc:
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"checkout.html",
|
||||
ctx(request, items=items, total=total, error=str(exc)),
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/order/{public_id}", response_class=HTMLResponse)
|
||||
def order_page(
|
||||
public_id: str,
|
||||
request: Request,
|
||||
email: str = "",
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
order = db.scalar(select(Order).where(Order.public_id == public_id.upper()))
|
||||
if not order:
|
||||
return RedirectResponse("/", status_code=303)
|
||||
if email and email.strip().lower() != order.email:
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"order_locked.html",
|
||||
ctx(request, public_id=public_id, error="Email не совпадает с заказом"),
|
||||
)
|
||||
if not email:
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"order_locked.html",
|
||||
ctx(request, public_id=public_id, error=None),
|
||||
)
|
||||
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"order.html",
|
||||
ctx(request, order=order),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/order/{public_id}", response_class=HTMLResponse)
|
||||
def order_unlock(
|
||||
public_id: str,
|
||||
request: Request,
|
||||
email: str = Form(...),
|
||||
):
|
||||
return RedirectResponse(
|
||||
f"/order/{public_id}?email={email.strip().lower()}",
|
||||
status_code=303,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/lookup", response_class=HTMLResponse)
|
||||
def lookup_page(request: Request):
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"lookup.html",
|
||||
ctx(request, error=None),
|
||||
)
|
||||
|
||||
|
||||
@router.post("/lookup")
|
||||
def lookup_submit(
|
||||
request: Request,
|
||||
public_id: str = Form(...),
|
||||
email: str = Form(...),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
order = db.scalar(select(Order).where(Order.public_id == public_id.strip().upper()))
|
||||
if not order or order.email != email.strip().lower():
|
||||
return router.templates.TemplateResponse( # type: ignore[attr-defined]
|
||||
request,
|
||||
"lookup.html",
|
||||
ctx(request, error="Заказ не найден. Проверьте номер и email."),
|
||||
status_code=404,
|
||||
)
|
||||
return RedirectResponse(
|
||||
f"/order/{order.public_id}?email={order.email}",
|
||||
status_code=303,
|
||||
)
|
||||
Reference in New Issue
Block a user