Add request access modal on login page with Telegram notification

- Modal form: name, company, email, phone (required), manager (optional), product checkboxes
- Products loaded from DB via GET /api/public/services-by-category (public route)
- POST /api/request-access sends styled Telegram message with divider and emojis
- Dark-themed modal matching login page design
- CSS: overlay, card, fields, checkbox list, error, footer buttons
This commit is contained in:
2026-05-14 06:22:39 +00:00
parent 9530f3e957
commit f740420a77
3 changed files with 419 additions and 1 deletions
+88
View File
@@ -18,11 +18,15 @@ from sqlalchemy import delete, select, text, update
from sqlalchemy.orm import Session
from starlette.responses import HTMLResponse as _HR
import urllib.request as _urllib_request
import urllib.parse as _urllib_parse
import json as _json
from config import (
COOKIE_NAME, CSRF_COOKIE, GO_POOL_LOCK_TIMEOUT_SECONDS,
GO_USER_LOCK_TIMEOUT_SECONDS, LOG_LEVEL, LOG_SLOW_REQUEST_MS,
MAX_ACTIVE_SERVICES_PER_USER, PUBLIC_HOST, SESSION_IDLE_SECONDS,
WEB_POOL_BUFFER, WEB_POOL_SIZE,
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, TELEGRAM_API_URL,
)
from database import get_db
from models import (
@@ -394,6 +398,90 @@ def admin_page(request: Request, admin: User = Depends(require_admin), db: Sessi
)
@app.get("/api/public/services-by-category")
def public_services_by_category(db: Session = Depends(get_db)):
services = db.execute(
select(Service).where(Service.active == True).order_by(Service.name)
).scalars().all()
categories = db.execute(select(Category).order_by(Category.name)).scalars().all()
cat_map = {c.id: c.name for c in categories}
svc_cats: dict[int, list[str]] = {}
links = db.execute(select(ServiceCategory)).scalars().all()
for lnk in links:
svc_cats.setdefault(lnk.service_id, []).append(cat_map.get(lnk.category_id, ""))
result: dict[str, list[dict]] = {"Без категории": []}
for svc in services:
cats = svc_cats.get(svc.id, [])
entry = {"id": svc.id, "name": svc.name}
if cats:
for cat in cats:
result.setdefault(cat, []).append(entry)
else:
result["Без категории"].append(entry)
if not result["Без категории"]:
del result["Без категории"]
return result
@app.post("/api/request-access")
async def request_access(request: Request, db: Session = Depends(get_db)):
try:
data = await request.json()
except Exception:
raise HTTPException(status_code=400, detail="Invalid JSON")
name = str(data.get("name", "")).strip()
company = str(data.get("company", "")).strip()
email = str(data.get("email", "")).strip()
phone = str(data.get("phone", "")).strip()
manager = str(data.get("manager", "")).strip()
products = data.get("products", [])
if not name or not company or not email or not phone:
raise HTTPException(status_code=422, detail="Заполните все обязательные поля")
products_text = ""
if products:
items = "\n".join(f"{p}" for p in products)
products_text = f"\n\n🖥 *Интересующие продукты:*\n{items}"
divider = "━━━━━━━━━━━━━━━━━━━━━━"
manager_text = f"\n🤝 *Менеджер МОНТ:* {manager}" if manager else ""
text = (
f"🔔 *Новый запрос доступа к полигону МОНТ*\n"
f"{divider}\n\n"
f"👤 *Имя:* {name}\n"
f"🏢 *Компания:* {company}\n"
f"📧 *Email:* {email}\n"
f"📱 *Телефон:* {phone}"
f"{manager_text}"
f"{products_text}"
)
if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID:
log_event("telegram_not_configured", {})
return {"ok": True}
try:
payload = _json.dumps({
"chat_id": TELEGRAM_CHAT_ID,
"text": text,
"parse_mode": "Markdown",
}).encode()
url = f"{TELEGRAM_API_URL}{TELEGRAM_BOT_TOKEN}/sendMessage"
req = _urllib_request.Request(url, data=payload, headers={"Content-Type": "application/json"})
with _urllib_request.urlopen(req, timeout=10) as resp:
resp.read()
except Exception as e:
log_event("telegram_send_error", {"error": str(e)})
raise HTTPException(status_code=502, detail="Ошибка отправки запроса")
return {"ok": True}
@app.post("/login")
def login(
request: Request,