harden telegram webapp production readiness
This commit is contained in:
@@ -9,6 +9,12 @@ class ApiClient:
|
||||
def __init__(self) -> None:
|
||||
self.base_url = settings.api_base_url.rstrip("/")
|
||||
|
||||
def headers(self, telegram_id: int | None = None) -> dict[str, str]:
|
||||
headers = {"X-Internal-API-Token": settings.internal_api_token}
|
||||
if telegram_id is not None:
|
||||
headers["X-Telegram-User-Id"] = str(telegram_id)
|
||||
return headers
|
||||
|
||||
async def upsert_user(self, telegram_user: Any) -> dict[str, Any]:
|
||||
payload = {
|
||||
"telegram_id": telegram_user.id,
|
||||
@@ -17,24 +23,30 @@ class ApiClient:
|
||||
"last_name": telegram_user.last_name,
|
||||
}
|
||||
async with httpx.AsyncClient(base_url=self.base_url, timeout=10) as client:
|
||||
response = await client.post("/api/users", json=payload)
|
||||
response = await client.post("/api/users", json=payload, headers=self.headers())
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
async def list_cars(self, owner_id: int) -> list[dict[str, Any]]:
|
||||
async def list_cars(self, owner_id: int, telegram_id: int) -> list[dict[str, Any]]:
|
||||
async with httpx.AsyncClient(base_url=self.base_url, timeout=10) as client:
|
||||
response = await client.get("/api/cars", params={"owner_id": owner_id})
|
||||
response = await client.get(
|
||||
"/api/cars", params={"owner_id": owner_id}, headers=self.headers(telegram_id)
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
async def create_car(self, owner_id: int, name: str) -> dict[str, Any]:
|
||||
async def create_car(self, owner_id: int, name: str, telegram_id: int) -> dict[str, Any]:
|
||||
async with httpx.AsyncClient(base_url=self.base_url, timeout=10) as client:
|
||||
response = await client.post("/api/cars", json={"owner_id": owner_id, "name": name})
|
||||
response = await client.post(
|
||||
"/api/cars",
|
||||
json={"owner_id": owner_id, "name": name},
|
||||
headers=self.headers(telegram_id),
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
async def stats(self, car_id: int) -> dict[str, Any]:
|
||||
async def stats(self, car_id: int, telegram_id: int) -> dict[str, Any]:
|
||||
async with httpx.AsyncClient(base_url=self.base_url, timeout=10) as client:
|
||||
response = await client.get(f"/api/cars/{car_id}/stats")
|
||||
response = await client.get(f"/api/cars/{car_id}/stats", headers=self.headers(telegram_id))
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
11
bot/main.py
11
bot/main.py
@@ -25,7 +25,7 @@ api = ApiClient()
|
||||
def main_keyboard() -> ReplyKeyboardMarkup:
|
||||
return ReplyKeyboardMarkup(
|
||||
keyboard=[
|
||||
[KeyboardButton(text="Открыть гараж", web_app=WebAppInfo(url=settings.webapp_url))],
|
||||
[KeyboardButton(text="Открыть гараж", web_app=WebAppInfo(url=settings.effective_webapp_url))],
|
||||
[KeyboardButton(text="Мои авто"), KeyboardButton(text="Помощь")],
|
||||
],
|
||||
resize_keyboard=True,
|
||||
@@ -50,7 +50,7 @@ async def add_car(message: Message, command: CommandObject) -> None:
|
||||
if not name:
|
||||
await message.answer("Напиши так: /add_car Toyota Camry")
|
||||
return
|
||||
car = await api.create_car(user["id"], name)
|
||||
car = await api.create_car(user["id"], name, message.from_user.id)
|
||||
await message.answer(f"Добавил авто: {car['name']}")
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ async def add_car(message: Message, command: CommandObject) -> None:
|
||||
@dp.message(F.text == "Мои авто")
|
||||
async def cars(message: Message) -> None:
|
||||
user = await api.upsert_user(message.from_user)
|
||||
items = await api.list_cars(user["id"])
|
||||
items = await api.list_cars(user["id"], message.from_user.id)
|
||||
if not items:
|
||||
await message.answer("Автомобилей пока нет. Добавь через mini app или командой /add_car Название.")
|
||||
return
|
||||
@@ -72,7 +72,7 @@ async def cars(message: Message) -> None:
|
||||
@dp.callback_query(F.data.startswith("stats:"))
|
||||
async def show_stats(callback: CallbackQuery) -> None:
|
||||
car_id = int(callback.data.split(":", 1)[1])
|
||||
stats = await api.stats(car_id)
|
||||
stats = await api.stats(car_id, callback.from_user.id)
|
||||
consumption = stats["avg_consumption_l_per_100km"]
|
||||
cost_per_km = stats["cost_per_km"]
|
||||
await callback.message.answer(
|
||||
@@ -106,6 +106,9 @@ async def help_message(message: Message) -> None:
|
||||
async def main() -> None:
|
||||
if not settings.bot_token:
|
||||
raise RuntimeError("BOT_TOKEN is empty")
|
||||
if not settings.internal_api_token:
|
||||
raise RuntimeError("INTERNAL_API_TOKEN is empty")
|
||||
settings.validate_webapp_url_for_telegram()
|
||||
bot = Bot(settings.bot_token)
|
||||
await dp.start_polling(bot)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user