flpolprojects commited on
Commit
90b8920
·
verified ·
1 Parent(s): 2e8fce9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +171 -73
app.py CHANGED
@@ -6,7 +6,7 @@ from datetime import datetime
6
  from aiogram import Bot, Dispatcher, types, F
7
  from aiogram.filters import Command
8
  from aiogram.utils.keyboard import ReplyKeyboardBuilder, InlineKeyboardBuilder
9
- from flask import Flask, request, jsonify, render_template_string
10
  import logging
11
  import threading
12
  from huggingface_hub import HfApi, hf_hub_download
@@ -23,7 +23,7 @@ bot = Bot(token=BOT_TOKEN)
23
  dp = Dispatcher()
24
  app = Flask(__name__)
25
 
26
- # Путь для хранения данных (товары и заказы)
27
  DATA_FILE = 'data.json'
28
 
29
  # Настройки Hugging Face
@@ -37,23 +37,25 @@ def load_data():
37
  download_db_from_hf()
38
  with open(DATA_FILE, 'r', encoding='utf-8') as f:
39
  loaded_data = json.load(f)
40
- # Проверка структуры JSON: ожидается словарь с ключами 'products' и 'orders'
41
  if not (isinstance(loaded_data, dict) and 'products' in loaded_data and 'orders' in loaded_data):
42
  logger.error("Неверная структура JSON файла, ожидается словарь с ключами 'products' и 'orders'. Используем дефолтное значение.")
43
- return {'products': [], 'orders': []}
 
 
44
  return loaded_data
45
  except FileNotFoundError:
46
  logger.warning("Локальный файл базы данных не найден после скачивания.")
47
- return {'products': [], 'orders': []}
48
  except json.JSONDecodeError:
49
  logger.error("Ошибка при загрузке данных: Невозможно декодировать JSON файл.")
50
- return {'products': [], 'orders': []}
51
  except RepositoryNotFoundError:
52
  logger.error("Репозиторий не найден. Создание локальной базы данных.")
53
- return {'products': [], 'orders': []}
54
  except Exception as e:
55
  logger.error(f"Ошибка при загрузке данных: {e}")
56
- return {'products': [], 'orders': []}
57
 
58
  # Функция для сохранения данных в Hugging Face Hub
59
  def save_data(data):
@@ -102,8 +104,9 @@ def download_db_from_hf():
102
  # Загрузка данных
103
  data = load_data()
104
 
105
- # Клавиатуры
106
  def get_main_keyboard():
 
107
  builder = ReplyKeyboardBuilder()
108
  builder.button(text="📋 Меню")
109
  builder.button(text="🛒 Корзина")
@@ -111,6 +114,14 @@ def get_main_keyboard():
111
  builder.adjust(2)
112
  return builder.as_markup(resize_keyboard=True)
113
 
 
 
 
 
 
 
 
 
114
  def get_product_keyboard(product_id):
115
  builder = InlineKeyboardBuilder()
116
  builder.button(text="Добавить в корзину", callback_data=f"add_{product_id}")
@@ -121,22 +132,42 @@ def get_product_keyboard(product_id):
121
  async def cmd_start(message: types.Message):
122
  await message.answer("Привет! Я твой бот-магазин. Выбери действие:", reply_markup=get_main_keyboard())
123
 
 
124
  @dp.message(F.text == "📋 Меню")
125
- async def show_products(message: types.Message):
126
- if not data['products']:
127
- await message.answer("Нет доступных товаров.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  return
129
- for product in data['products']:
 
130
  photo_url = f"https://huggingface.co/datasets/{REPO_ID}/resolve/main/photos/{product['photo']}" if product.get('photo') else None
131
  caption = f"🏷 {product['name']} - {product['price']} руб.\nОписание: {product['description']}\n/id: {product['id']}"
132
  if photo_url:
133
  try:
134
- await bot.send_photo(chat_id=message.chat.id, photo=photo_url, caption=caption, reply_markup=get_product_keyboard(product['id']))
135
  except Exception as e:
136
  logger.error(f"Ошибка при отправке фото: {e}")
137
- await message.answer(caption, reply_markup=get_product_keyboard(product['id']))
138
  else:
139
- await message.answer(caption, reply_markup=get_product_keyboard(product['id']))
 
140
 
141
  @dp.message(F.text == "🛒 Корзина")
142
  async def show_cart(message: types.Message):
@@ -158,7 +189,11 @@ async def show_cart(message: types.Message):
158
 
159
  @dp.callback_query(F.data.startswith("add_"))
160
  async def add_to_cart(callback_query: types.CallbackQuery):
161
- product_id = int(callback_query.data.split('_')[1])
 
 
 
 
162
  product = next((p for p in data['products'] if p['id'] == product_id), None)
163
  if product:
164
  user_id = callback_query.from_user.id
@@ -175,7 +210,11 @@ async def add_to_cart(callback_query: types.CallbackQuery):
175
  # Изменённый обработчик "Оформить заказ" – заказ сразу отправляется в WhatsApp и удаляется из корзины
176
  @dp.callback_query(F.data.startswith("complete_"))
177
  async def complete_order(callback_query: types.CallbackQuery):
178
- user_id = int(callback_query.data.split('_')[1])
 
 
 
 
179
  cart = next((o for o in data['orders'] if o['user_id'] == user_id and not o.get('completed')), None)
180
  if cart and cart['items']:
181
  total = 0
@@ -187,11 +226,11 @@ async def complete_order(callback_query: types.CallbackQuery):
187
  total += product['price'] * item['quantity']
188
  cart_text += f"\nИтого: {total} руб."
189
  encoded_text = urllib.parse.quote(cart_text)
190
- whatsapp_link = f"https://wa.me/996500398754?text={encoded_text}"
 
191
  # Удаляем корзину, чтобы заказ не попадал в личный кабинет
192
  data['orders'].remove(cart)
193
  save_data(data)
194
- # Отправляем пользователю сообщение с ссылкой на WhatsApp
195
  await bot.send_message(user_id, f"Пожалуйста, оформите заказ через WhatsApp, перейдя по ссылке:\n{whatsapp_link}")
196
  await bot.answer_callback_query(callback_query.id)
197
  else:
@@ -200,7 +239,7 @@ async def complete_order(callback_query: types.CallbackQuery):
200
  @dp.message(F.text == "📦 Заказы")
201
  async def show_orders(message: types.Message):
202
  user_id = message.from_user.id
203
- # Поскольку оформленные заказы больше не сохраняются, здесь отображаются только те, которые остались в базе (если вдруг)
204
  user_orders = [o for o in data['orders'] if o.get('completed')]
205
  if not user_orders:
206
  await message.answer("У вас нет оформленных заказов.")
@@ -215,7 +254,7 @@ async def show_orders(message: types.Message):
215
  response += f"\nИтого: {total} руб.\nДата: {order['date']}"
216
  await message.answer(response)
217
 
218
- # Админ-панель (Flask) с HTML, CSS и JavaScript в строке
219
  admin_html = """
220
  <!DOCTYPE html>
221
  <html>
@@ -224,64 +263,98 @@ admin_html = """
224
  <style>
225
  body { font-family: Arial, sans-serif; margin: 20px; background-color: #f0f0f0; }
226
  .container { max-width: 800px; margin: 0 auto; }
227
- .product { border: 1px solid #ccc; padding: 15px; margin: 10px 0; background-color: #fff; border-radius: 5px; }
228
- form { background-color: #fff; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
229
- input, textarea { width: 100%; margin: 5px 0; padding: 8px; }
230
  button { background-color: #4CAF50; color: white; padding: 10px 15px; border: none; border-radius: 5px; cursor: pointer; }
231
  button:hover { background-color: #45a049; }
232
  img { max-width: 100px; max-height: 100px; }
 
233
  </style>
234
  </head>
235
  <body>
236
  <div class="container">
237
- <h1>Управление товарами</h1>
238
- <form id="addProductForm" method="POST" enctype="multipart/form-data" action="/add_product">
239
- <input type="text" name="name" placeholder="Название" required><br>
240
- <input type="number" name="price" placeholder="Цена" step="0.01" required><br>
241
- <textarea name="description" placeholder="Описание" required></textarea><br>
242
- <input type="file" name="photo" accept="image/*"><br>
243
- <button type="submit">Добавить товар</button>
244
- </form>
245
- <h2>Существующие товары</h2>
246
- {% if products %}
247
- {% for product in products %}
248
- <div class="product">
249
- {{ product.name }} - {{ product.price }} руб.<br>
250
- {{ product.description }}<br>
251
- {% if product.photo %}
252
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product.photo }}" alt="{{ product.name }}">
253
- {% endif %}
254
- <button onclick="deleteProduct({{ product.id }})">Удалить</button>
255
- </div>
256
- {% endfor %}
257
- {% else %}
258
- <p>Нет товаров.</p>
259
- {% endif %}
260
- <h2>Заказы</h2>
261
- {% if orders %}
262
- {% for order in orders %}
263
- <div class="product">
264
- Пользователь: {{ order.user_id }}<br>
265
- Дата: {{ order.date }}<br>
266
- Товары:
267
- {% for item in order['items'] %}
268
- {% for product in products %}
269
- {% if product.id == item.product_id %}
270
- {{ item.quantity }} x {{ product.name }}<br>
271
- {% endif %}
272
  {% endfor %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  {% endfor %}
274
- </div>
275
- {% endfor %}
276
- {% else %}
277
- <p>Нет заказов.</p>
278
- {% endif %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  </div>
280
  <script>
281
  async function deleteProduct(productId) {
282
  const response = await fetch(`/delete_product/${productId}`, { method: 'POST' });
283
  if (response.ok) window.location.reload();
284
  }
 
 
 
 
285
  </script>
286
  </body>
287
  </html>
@@ -290,8 +363,8 @@ admin_html = """
290
  @app.route('/')
291
  def admin_panel():
292
  try:
293
- logger.info("Rendering admin panel with products and orders")
294
- return render_template_string(admin_html, products=data['products'], orders=data['orders'], repo_id=REPO_ID)
295
  except Exception as e:
296
  logger.error(f"Ошибка в шаблоне: {e}")
297
  return "Ошибка сервера. Проверь логи.", 500
@@ -304,13 +377,14 @@ def add_product():
304
  name = request.form['name']
305
  price = float(request.form['price'])
306
  description = request.form['description']
307
- photo = request.files['photo'] # Получаем файл фотографии
 
308
  product_id = max((p['id'] for p in data['products']), default=0) + 1
309
 
310
  photo_filename = None
311
- if photo:
312
  photo_filename = secure_filename(photo.filename)
313
- temp_path = os.path.join(".", photo_filename) # Сохраняем временно локально
314
  photo.save(temp_path)
315
 
316
  try:
@@ -328,17 +402,18 @@ def add_product():
328
  logger.error(f"Ошибка при загрузке фото: {e}")
329
  return jsonify({'status': 'error', 'message': f'Ошибка при загрузке фото: {str(e)}'}), 500
330
  finally:
331
- os.remove(temp_path) # Удаляем временный файл
332
 
333
  data['products'].append({
334
  'id': product_id,
335
  'name': name,
336
  'price': price,
337
  'description': description,
 
338
  'photo': photo_filename
339
  })
340
  save_data(data)
341
- return jsonify({'status': 'success'})
342
  except Exception as e:
343
  logger.error(f"Ошибка при добавлении товара: {e}")
344
  return jsonify({'status': 'error', 'message': str(e)}), 500
@@ -354,6 +429,29 @@ def delete_product(product_id):
354
  logger.error(f"Ошибка при удалении товара: {e}")
355
  return jsonify({'status': 'error', 'message': str(e)}), 500
356
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  # Запуск бота и Flask
358
  async def on_startup(_):
359
  logger.info("Бот запущен!")
@@ -377,4 +475,4 @@ if __name__ == '__main__':
377
  except KeyboardInterrupt:
378
  logger.info("Stopping bot and Flask")
379
  finally:
380
- flask_thread.join() # Ждём завершения потока Flask при завершении программы
 
6
  from aiogram import Bot, Dispatcher, types, F
7
  from aiogram.filters import Command
8
  from aiogram.utils.keyboard import ReplyKeyboardBuilder, InlineKeyboardBuilder
9
+ from flask import Flask, request, jsonify, render_template_string, redirect
10
  import logging
11
  import threading
12
  from huggingface_hub import HfApi, hf_hub_download
 
23
  dp = Dispatcher()
24
  app = Flask(__name__)
25
 
26
+ # Путь для хранения данных (товары, заказы и категории)
27
  DATA_FILE = 'data.json'
28
 
29
  # Настройки Hugging Face
 
37
  download_db_from_hf()
38
  with open(DATA_FILE, 'r', encoding='utf-8') as f:
39
  loaded_data = json.load(f)
40
+ # Проверка структуры JSON: ожидается словарь с ключами 'products', 'orders' и 'categories'
41
  if not (isinstance(loaded_data, dict) and 'products' in loaded_data and 'orders' in loaded_data):
42
  logger.error("Неверная структура JSON файла, ожидается словарь с ключами 'products' и 'orders'. Используем дефолтное значение.")
43
+ loaded_data = {'products': [], 'orders': []}
44
+ if "categories" not in loaded_data:
45
+ loaded_data["categories"] = []
46
  return loaded_data
47
  except FileNotFoundError:
48
  logger.warning("Локальный файл базы данных не найден после скачивания.")
49
+ return {'products': [], 'orders': [], 'categories': []}
50
  except json.JSONDecodeError:
51
  logger.error("Ошибка при загрузке данных: Невозможно декодировать JSON файл.")
52
+ return {'products': [], 'orders': [], 'categories': []}
53
  except RepositoryNotFoundError:
54
  logger.error("Репозиторий не найден. Создание локальной базы данных.")
55
+ return {'products': [], 'orders': [], 'categories': []}
56
  except Exception as e:
57
  logger.error(f"Ошибка при загрузке данных: {e}")
58
+ return {'products': [], 'orders': [], 'categories': []}
59
 
60
  # Функция для сохранения данных в Hugging Face Hub
61
  def save_data(data):
 
104
  # Загрузка данных
105
  data = load_data()
106
 
107
+ # Функции для формирования клавиатур
108
  def get_main_keyboard():
109
+ # Теперь кнопка "Меню" покажет категории товаров
110
  builder = ReplyKeyboardBuilder()
111
  builder.button(text="📋 Меню")
112
  builder.button(text="🛒 Корзина")
 
114
  builder.adjust(2)
115
  return builder.as_markup(resize_keyboard=True)
116
 
117
+ def get_category_keyboard():
118
+ builder = InlineKeyboardBuilder()
119
+ for category in data['categories']:
120
+ builder.button(text=category['name'], callback_data=f"cat_{category['id']}")
121
+ # Добавляем кнопку "Назад" для возврата в главное меню, если понадобится
122
+ builder.adjust(2)
123
+ return builder.as_markup()
124
+
125
  def get_product_keyboard(product_id):
126
  builder = InlineKeyboardBuilder()
127
  builder.button(text="Добавить в корзину", callback_data=f"add_{product_id}")
 
132
  async def cmd_start(message: types.Message):
133
  await message.answer("Привет! Я твой бот-магазин. Выбери действие:", reply_markup=get_main_keyboard())
134
 
135
+ # При нажатии на "Меню" выводятся категории
136
  @dp.message(F.text == "📋 Меню")
137
+ async def show_categories(message: types.Message):
138
+ if not data['categories']:
139
+ await message.answer("Нет доступных категорий.")
140
+ return
141
+ await message.answer("Выберите категорию:", reply_markup=get_category_keyboard())
142
+
143
+ # Обработчик для выбора категории (callback_data: "cat_{id}")
144
+ @dp.callback_query(F.data.startswith("cat_"))
145
+ async def show_products_in_category(callback_query: types.CallbackQuery):
146
+ try:
147
+ cat_id = int(callback_query.data.split('_')[1])
148
+ except (IndexError, ValueError):
149
+ await bot.answer_callback_query(callback_query.id, "Неверные данные категории")
150
+ return
151
+
152
+ # Фильтруем товары по выбранной категории
153
+ products_in_cat = [p for p in data['products'] if p.get('category_id') == cat_id]
154
+ if not products_in_cat:
155
+ await bot.send_message(callback_query.from_user.id, "В этой категории нет товаров.")
156
+ await bot.answer_callback_query(callback_query.id)
157
  return
158
+
159
+ for product in products_in_cat:
160
  photo_url = f"https://huggingface.co/datasets/{REPO_ID}/resolve/main/photos/{product['photo']}" if product.get('photo') else None
161
  caption = f"🏷 {product['name']} - {product['price']} руб.\nОписание: {product['description']}\n/id: {product['id']}"
162
  if photo_url:
163
  try:
164
+ await bot.send_photo(chat_id=callback_query.from_user.id, photo=photo_url, caption=caption, reply_markup=get_product_keyboard(product['id']))
165
  except Exception as e:
166
  logger.error(f"Ошибка при отправке фото: {e}")
167
+ await bot.send_message(callback_query.from_user.id, caption, reply_markup=get_product_keyboard(product['id']))
168
  else:
169
+ await bot.send_message(callback_query.from_user.id, caption, reply_markup=get_product_keyboard(product['id']))
170
+ await bot.answer_callback_query(callback_query.id)
171
 
172
  @dp.message(F.text == "🛒 Корзина")
173
  async def show_cart(message: types.Message):
 
189
 
190
  @dp.callback_query(F.data.startswith("add_"))
191
  async def add_to_cart(callback_query: types.CallbackQuery):
192
+ try:
193
+ product_id = int(callback_query.data.split('_')[1])
194
+ except (IndexError, ValueError):
195
+ await bot.answer_callback_query(callback_query.id, "Неверные данные товара")
196
+ return
197
  product = next((p for p in data['products'] if p['id'] == product_id), None)
198
  if product:
199
  user_id = callback_query.from_user.id
 
210
  # Изменённый обработчик "Оформить заказ" – заказ сразу отправляется в WhatsApp и удаляется из корзины
211
  @dp.callback_query(F.data.startswith("complete_"))
212
  async def complete_order(callback_query: types.CallbackQuery):
213
+ try:
214
+ user_id = int(callback_query.data.split('_')[1])
215
+ except (IndexError, ValueError):
216
+ await bot.answer_callback_query(callback_query.id, "Неверные данные")
217
+ return
218
  cart = next((o for o in data['orders'] if o['user_id'] == user_id and not o.get('completed')), None)
219
  if cart and cart['items']:
220
  total = 0
 
226
  total += product['price'] * item['quantity']
227
  cart_text += f"\nИтого: {total} руб."
228
  encoded_text = urllib.parse.quote(cart_text)
229
+ # Новый номер WhatsApp: +996709513331 (без знака + для ссылки)
230
+ whatsapp_link = f"https://wa.me/996709513331?text={encoded_text}"
231
  # Удаляем корзину, чтобы заказ не попадал в личный кабинет
232
  data['orders'].remove(cart)
233
  save_data(data)
 
234
  await bot.send_message(user_id, f"Пожалуйста, оформите заказ через WhatsApp, перейдя по ссылке:\n{whatsapp_link}")
235
  await bot.answer_callback_query(callback_query.id)
236
  else:
 
239
  @dp.message(F.text == "📦 Заказы")
240
  async def show_orders(message: types.Message):
241
  user_id = message.from_user.id
242
+ # Так как оформленные заказы больше не сохраняются, здесь показываются только те, которые остались (если вдруг)
243
  user_orders = [o for o in data['orders'] if o.get('completed')]
244
  if not user_orders:
245
  await message.answer("У вас нет оформленных заказов.")
 
254
  response += f"\nИтого: {total} руб.\nДата: {order['date']}"
255
  await message.answer(response)
256
 
257
+ # Админ-панель (Flask)
258
  admin_html = """
259
  <!DOCTYPE html>
260
  <html>
 
263
  <style>
264
  body { font-family: Arial, sans-serif; margin: 20px; background-color: #f0f0f0; }
265
  .container { max-width: 800px; margin: 0 auto; }
266
+ .section { background-color: #fff; padding: 15px; margin-bottom: 20px; border-radius: 5px; }
267
+ input, textarea, select { width: 100%; margin: 5px 0; padding: 8px; }
 
268
  button { background-color: #4CAF50; color: white; padding: 10px 15px; border: none; border-radius: 5px; cursor: pointer; }
269
  button:hover { background-color: #45a049; }
270
  img { max-width: 100px; max-height: 100px; }
271
+ .item { border: 1px solid #ccc; padding: 10px; margin: 5px 0; }
272
  </style>
273
  </head>
274
  <body>
275
  <div class="container">
276
+ <h1>Админ-панель</h1>
277
+ <div class="section">
278
+ <h2>Управление категориями</h2>
279
+ <form id="addCategoryForm" method="POST" action="/add_category">
280
+ <input type="text" name="name" placeholder="Название категории" required><br>
281
+ <button type="submit">Добавить категорию</button>
282
+ </form>
283
+ <h3>Существующие категории</h3>
284
+ {% if categories %}
285
+ {% for category in categories %}
286
+ <div class="item">
287
+ {{ category.name }} (ID: {{ category.id }})
288
+ <button onclick="deleteCategory({{ category.id }})">Удалить</button>
289
+ </div>
290
+ {% endfor %}
291
+ {% else %}
292
+ <p>Нет категорий.</p>
293
+ {% endif %}
294
+ </div>
295
+ <div class="section">
296
+ <h2>Управление товарами</h2>
297
+ <form id="addProductForm" method="POST" enctype="multipart/form-data" action="/add_product">
298
+ <input type="text" name="name" placeholder="Название" required><br>
299
+ <input type="number" name="price" placeholder="Цена" step="0.01" required><br>
300
+ <textarea name="description" placeholder="Описание" required></textarea><br>
301
+ <label>Категория:</label>
302
+ <select name="category_id" required>
303
+ <option value="">Выберите категорию</option>
304
+ {% for category in categories %}
305
+ <option value="{{ category.id }}">{{ category.name }}</option>
 
 
 
 
 
306
  {% endfor %}
307
+ </select><br>
308
+ <input type="file" name="photo" accept="image/*"><br>
309
+ <button type="submit">Добавить товар</button>
310
+ </form>
311
+ <h3>Существующие товары</h3>
312
+ {% if products %}
313
+ {% for product in products %}
314
+ <div class="item">
315
+ {{ product.name }} - {{ product.price }} руб.<br>
316
+ {{ product.description }}<br>
317
+ {% if product.photo %}
318
+ <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product.photo }}" alt="{{ product.name }}">
319
+ {% endif %}
320
+ <button onclick="deleteProduct({{ product.id }})">Удалить</button>
321
+ </div>
322
  {% endfor %}
323
+ {% else %}
324
+ <p>Нет товаров.</p>
325
+ {% endif %}
326
+ </div>
327
+ <div class="section">
328
+ <h2>Заказы</h2>
329
+ {% if orders %}
330
+ {% for order in orders %}
331
+ <div class="item">
332
+ Пользователь: {{ order.user_id }}<br>
333
+ Дата: {{ order.date }}<br>
334
+ Товары:
335
+ {% for item in order['items'] %}
336
+ {% for product in products %}
337
+ {% if product.id == item.product_id %}
338
+ {{ item.quantity }} x {{ product.name }}<br>
339
+ {% endif %}
340
+ {% endfor %}
341
+ {% endfor %}
342
+ </div>
343
+ {% endfor %}
344
+ {% else %}
345
+ <p>Нет заказов.</p>
346
+ {% endif %}
347
+ </div>
348
  </div>
349
  <script>
350
  async function deleteProduct(productId) {
351
  const response = await fetch(`/delete_product/${productId}`, { method: 'POST' });
352
  if (response.ok) window.location.reload();
353
  }
354
+ async function deleteCategory(categoryId) {
355
+ const response = await fetch(`/delete_category/${categoryId}`, { method: 'POST' });
356
+ if (response.ok) window.location.reload();
357
+ }
358
  </script>
359
  </body>
360
  </html>
 
363
  @app.route('/')
364
  def admin_panel():
365
  try:
366
+ logger.info("Rendering admin panel with products, orders and categories")
367
+ return render_template_string(admin_html, products=data['products'], orders=data['orders'], categories=data['categories'], repo_id=REPO_ID)
368
  except Exception as e:
369
  logger.error(f"Ошибка в шаблоне: {e}")
370
  return "Ошибка сервера. Проверь логи.", 500
 
377
  name = request.form['name']
378
  price = float(request.form['price'])
379
  description = request.form['description']
380
+ category_id = int(request.form['category_id'])
381
+ photo = request.files.get('photo')
382
  product_id = max((p['id'] for p in data['products']), default=0) + 1
383
 
384
  photo_filename = None
385
+ if photo and photo.filename:
386
  photo_filename = secure_filename(photo.filename)
387
+ temp_path = os.path.join(".", photo_filename)
388
  photo.save(temp_path)
389
 
390
  try:
 
402
  logger.error(f"Ошибка при загрузке фото: {e}")
403
  return jsonify({'status': 'error', 'message': f'Ошибка при загрузке фото: {str(e)}'}), 500
404
  finally:
405
+ os.remove(temp_path)
406
 
407
  data['products'].append({
408
  'id': product_id,
409
  'name': name,
410
  'price': price,
411
  'description': description,
412
+ 'category_id': category_id,
413
  'photo': photo_filename
414
  })
415
  save_data(data)
416
+ return redirect("/")
417
  except Exception as e:
418
  logger.error(f"Ошибка при добавлении товара: {e}")
419
  return jsonify({'status': 'error', 'message': str(e)}), 500
 
429
  logger.error(f"Ошибка при удалении товара: {e}")
430
  return jsonify({'status': 'error', 'message': str(e)}), 500
431
 
432
+ @app.route('/add_category', methods=['POST'])
433
+ def add_category():
434
+ try:
435
+ name = request.form['name']
436
+ category_id = max((c['id'] for c in data['categories']), default=0) + 1
437
+ data['categories'].append({'id': category_id, 'name': name})
438
+ save_data(data)
439
+ return redirect("/")
440
+ except Exception as e:
441
+ logger.error(f"Ошибка при добавлении категории: {e}")
442
+ return jsonify({'status': 'error', 'message': str(e)}), 500
443
+
444
+ @app.route('/delete_category/<int:category_id>', methods=['POST'])
445
+ def delete_category(category_id):
446
+ try:
447
+ logger.info(f"Deleting category with id={category_id}")
448
+ data['categories'] = [c for c in data['categories'] if c['id'] != category_id]
449
+ save_data(data)
450
+ return jsonify({'status': 'success'})
451
+ except Exception as e:
452
+ logger.error(f"Ошибка при удалении категории: {e}")
453
+ return jsonify({'status': 'error', 'message': str(e)}), 500
454
+
455
  # Запуск бота и Flask
456
  async def on_startup(_):
457
  logger.info("Бот запущен!")
 
475
  except KeyboardInterrupt:
476
  logger.info("Stopping bot and Flask")
477
  finally:
478
+ flask_thread.join()