flpolprojects commited on
Commit
1a917d0
·
verified ·
1 Parent(s): 38ba303

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -70
app.py CHANGED
@@ -1,4 +1,4 @@
1
- from flask import Flask, render_template_string, request, redirect, url_for, send_from_directory
2
  import json
3
  import os
4
  import logging
@@ -27,63 +27,45 @@ def load_data():
27
  download_db_from_hf()
28
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
29
  data = json.load(file)
30
- logging.info("Данные успешно загружены из JSON")
31
  if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
32
- # Attempt to handle older formats gracefully if possible, or default
33
  if isinstance(data, list):
34
- # Assume it was just a list of products in older format
35
  return {'products': data, 'categories': []}
36
  else:
37
- # Fallback for unexpected format
38
- logging.warning("Неожиданный формат данных в JSON.")
39
  return {'products': [], 'categories': []}
40
  return data
41
  except FileNotFoundError:
42
- logging.warning(f"Локальный файл базы данных ({DATA_FILE}) не найден после скачивания.")
43
  return {'products': [], 'categories': []}
44
  except json.JSONDecodeError:
45
- logging.error("Ошибка: Невозможно декодировать JSON файл.")
46
  return {'products': [], 'categories': []}
47
  except RepositoryNotFoundError:
48
- logging.error(f"Репозиторий '{REPO_ID}' не найден на Hugging Face. Создание локальной базы данных.")
49
  return {'products': [], 'categories': []}
50
  except HfHubHTTPError as e:
51
- logging.error(f"Ошибка HTTP при скачивании из Hugging Face: {e}")
52
- logging.warning(f"Попытка загрузить локальный файл '{DATA_FILE}' если существует.")
53
  try:
54
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
55
  data = json.load(file)
56
- logging.info("Данные успешно загружены из локального JSON после ошибки HF.")
57
  if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
58
  if isinstance(data, list):
59
  return {'products': data, 'categories': []}
60
  else:
61
- logging.warning("Неожиданный формат данных в локальном JSON.")
62
  return {'products': [], 'categories': []}
63
  return data
64
  except FileNotFoundError:
65
- logging.warning(f"Локальный файл '{DATA_FILE}' также не найден.")
66
  return {'products': [], 'categories': []}
67
  except json.JSONDecodeError:
68
- logging.error("Ошибка: Невозможно декодировать локальный JSON файл.")
69
  return {'products': [], 'categories': []}
70
  except Exception as e:
71
- logging.error(f"Произошла неизвестная ошибка при загрузке данных: {e}")
72
  return {'products': [], 'categories': []}
73
 
74
  def save_data(data):
75
  try:
76
  with open(DATA_FILE, 'w', encoding='utf-8') as file:
77
  json.dump(data, file, ensure_ascii=False, indent=4)
78
- logging.info("Данные успешно сохранены в JSON")
79
  upload_db_to_hf()
80
  except Exception as e:
81
- logging.error(f"Ошибка при сохранении данных: {e}")
82
  raise
83
 
84
  def upload_db_to_hf():
85
  if not HF_TOKEN_WRITE:
86
- logging.warning("HF_TOKEN не установлен. Пропуск загрузки на Hugging Face.")
87
  return
88
  try:
89
  api = HfApi()
@@ -95,13 +77,12 @@ def upload_db_to_hf():
95
  token=HF_TOKEN_WRITE,
96
  commit_message=f"Автоматическое резервное копирование базы данных {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
97
  )
98
- logging.info("Резервная копия JSON базы успешно загружена на Hugging Face.")
99
  except Exception as e:
100
  logging.error(f"Ошибка при загрузке резервной копии на Hugging Face: {e}")
101
 
 
102
  def download_db_from_hf():
103
  if not HF_TOKEN_READ:
104
- logging.warning("HF_TOKEN_READ не установлен. Пропуск скачивания из Hugging Face.")
105
  return
106
  try:
107
  hf_hub_download(
@@ -112,13 +93,10 @@ def download_db_from_hf():
112
  local_dir=".",
113
  local_dir_use_symlinks=False
114
  )
115
- logging.info("JSON база успешно скачана из Hugging Face.")
116
  except RepositoryNotFoundError:
117
- logging.warning(f"Репозиторий '{REPO_ID}' не найден. Скачива��ие невозможно.")
118
- raise # Re-raise to be caught by load_data
119
  except Exception as e:
120
- logging.error(f"Ошибка при скачивании JSON базы из Hugging Face: {e}")
121
- raise # Re-raise to be caught by load_data
122
 
123
  def periodic_backup():
124
  while True:
@@ -235,6 +213,16 @@ catalog_html = '''
235
  gap: 15px;
236
  padding: 10px;
237
  }
 
 
 
 
 
 
 
 
 
 
238
  .product {
239
  background: var(--light-text);
240
  border-radius: 15px;
@@ -297,7 +285,7 @@ catalog_html = '''
297
  flex-grow: 1;
298
  }
299
  .product-buttons {
300
- margin-top: auto; /* Push buttons to the bottom */
301
  }
302
  .product-button {
303
  display: block;
@@ -354,7 +342,7 @@ catalog_html = '''
354
  height: 100%;
355
  background-color: rgba(0,0,0,0.5);
356
  backdrop-filter: blur(5px);
357
- overflow: auto; /* Add scroll for smaller screens */
358
  }
359
  .modal-content {
360
  background: var(--light-text);
@@ -427,20 +415,20 @@ catalog_html = '''
427
 
428
  .quantity-input, .color-select {
429
  width: 100%;
430
- max-width: 150px; /* Adjust max-width */
431
  padding: 8px;
432
  border: 1px solid var(--secondary-color);
433
  border-radius: 8px;
434
  font-size: 1rem;
435
  margin: 5px 0;
436
- display: block; /* Make them block elements */
437
  margin-bottom: 15px;
438
  }
439
  .modal-content button {
440
- margin-top: 0; /* Remove extra margin */
441
  }
442
  .modal-content .product-button {
443
- margin-top: 15px; /* Restore margin for modal buttons */
444
  }
445
 
446
  .clear-cart {
@@ -457,6 +445,17 @@ catalog_html = '''
457
  background-color: #BA68C8;
458
  box-shadow: 0 4px 15px rgba(186, 104, 200, 0.4);
459
  }
 
 
 
 
 
 
 
 
 
 
 
460
  @media (max-width: 768px) {
461
  .header {
462
  flex-direction: column;
@@ -466,9 +465,6 @@ catalog_html = '''
466
  margin-right: 0;
467
  margin-bottom: 10px;
468
  }
469
- .products-grid {
470
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
471
- }
472
  .modal-content {
473
  margin: 10% auto;
474
  }
@@ -478,7 +474,7 @@ catalog_html = '''
478
  }
479
  @media (max-width: 480px) {
480
  .products-grid {
481
- grid-template-columns: 1fr; /* Stack products on very small screens */
482
  }
483
  .modal-content {
484
  margin: 15% auto;
@@ -558,11 +554,10 @@ catalog_html = '''
558
  </div>
559
  </div>
560
 
561
- <button id="cart-button" onclick="openCartModal()">🛒<span id="cart-count" style="position: absolute; top: -5px; right: -5px; background-color: red; color: white; border-radius: 50%; padding: 2px 5px; font-size: 0.7em; display: none;">0</span></button>
562
 
563
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.js"></script>
564
  <script>
565
- // products data is available as a global variable from the template
566
  const allProducts = {{ products|tojson }};
567
  let selectedProductName = null;
568
 
@@ -591,7 +586,7 @@ catalog_html = '''
591
  new Swiper('.swiper-container', {
592
  slidesPerView: 1,
593
  spaceBetween: 20,
594
- loop: true, // Enable loop if you have multiple images
595
  grabCursor: true,
596
  pagination: { el: '.swiper-pagination', clickable: true },
597
  navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
@@ -639,7 +634,7 @@ catalog_html = '''
639
 
640
  if (quantity <= 0) {
641
  alert("Укажите количество больше 0");
642
- quantityInput.value = 1; // Reset to 1
643
  return;
644
  }
645
 
@@ -682,7 +677,7 @@ catalog_html = '''
682
  if (cart.length > 0) {
683
  cartButton.style.display = 'block';
684
  cartCount.textContent = totalItems;
685
- cartCount.style.display = 'block';
686
  } else {
687
  cartButton.style.display = 'none';
688
  cartCount.style.display = 'none';
@@ -736,12 +731,11 @@ catalog_html = '''
736
  });
737
  orderText += `Итого к оплате: ${total} с`;
738
 
739
- const phoneNumber = '996772179559'; // Замените на ваш номер телефона в международном формате
740
  const whatsappUrl = `https://api.whatsapp.com/send?phone=${phoneNumber}&text=${encodeURIComponent(orderText)}`;
741
 
742
  window.open(whatsappUrl, '_blank');
743
- // Optionally clear cart after order attempt
744
- // clearCart();
745
  }
746
 
747
  function clearCart() {
@@ -776,13 +770,12 @@ catalog_html = '''
776
  const category = productDiv.getAttribute('data-category');
777
  const matchesSearch = name.includes(searchTerm) || description.includes(searchTerm);
778
  const matchesCategory = activeCategory === 'all' || category === activeCategory;
779
- productDiv.style.display = matchesSearch && matchesCategory ? 'flex' : 'none'; // Use flex because product uses flex-direction column
780
  });
781
  }
782
 
783
- // Initial load
784
  updateCartButton();
785
- filterProducts(); // Apply initial filter (all categories)
786
 
787
  </script>
788
  </body>
@@ -828,17 +821,14 @@ detail_html = '''
828
  @app.route('/')
829
  def catalog():
830
  data = load_data()
831
- # Sort products for display in the catalog by added_at, newest first
832
  products_for_display = sorted(data.get('products', []), key=lambda x: x.get('added_at', ''), reverse=True)
833
  categories = data.get('categories', [])
834
 
835
- # Pass the sorted list to the template
836
  return render_template_string(catalog_html, products=products_for_display, categories=categories, repo_id=REPO_ID, LOGO_URL=LOGO_URL)
837
 
838
  @app.route('/product_by_name/<string:product_name>')
839
  def product_detail_by_name(product_name):
840
  data = load_data()
841
- # Find the product by name in the *unsorted* list
842
  product = next((p for p in data.get('products', []) if p['name'] == product_name), None)
843
 
844
  if product:
@@ -868,7 +858,6 @@ def admin():
868
  category_name_to_delete = request.form.get('category_name_to_delete')
869
  if category_name_to_delete in categories:
870
  categories.remove(category_name_to_delete)
871
- # Change category for products using the deleted one
872
  for product in products:
873
  if product.get('category') == category_name_to_delete:
874
  product['category'] = 'Без категории'
@@ -877,7 +866,6 @@ def admin():
877
  else:
878
  return "Ошибка: Категория не найдена.", 400
879
  except Exception as e:
880
- logging.error(f"Ошибка при удалении категории: {e}")
881
  return f"Ошибка при удалении категории: {e}", 500
882
 
883
  elif action == 'add':
@@ -900,9 +888,7 @@ def admin():
900
  if photos_files:
901
  for photo in photos_files:
902
  if photo and photo.filename:
903
- # Ensure filename is secure and unique if needed (e.g., timestamp prefix)
904
  original_filename = secure_filename(photo.filename)
905
- # Use a timestamp or UUID to make filename unique in the repo
906
  timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f")
907
  photo_filename = f"{timestamp}_{original_filename}"
908
 
@@ -912,7 +898,6 @@ def admin():
912
  try:
913
  photo.save(temp_path)
914
  api = HfApi()
915
- # Upload to a dedicated 'photos' directory in the HF dataset repo
916
  api.upload_file(
917
  path_or_fileobj=temp_path,
918
  path_in_repo=f"photos/{photo_filename}",
@@ -924,10 +909,8 @@ def admin():
924
  photos_list.append(photo_filename)
925
  except Exception as e:
926
  logging.error(f"Ошибка при загрузке фото {original_filename} на HF: {e}")
927
- # Decide if you want to stop or continue with other photos
928
  pass
929
  finally:
930
- # Clean up the temporary file
931
  if os.path.exists(temp_path):
932
  os.remove(temp_path)
933
 
@@ -965,7 +948,6 @@ def admin():
965
  if not name or price is None or not description:
966
  return "Ошибка: Заполните все обязательные поля для редактирования", 400
967
 
968
- # Handle new photos
969
  if photos_files and any(photo.filename for photo in photos_files):
970
  new_photos_list = []
971
  for photo in photos_files:
@@ -995,7 +977,6 @@ def admin():
995
  if os.path.exists(temp_path):
996
  os.remove(temp_path)
997
  product_to_edit['photos'] = new_photos_list
998
- # else: no new photos uploaded, keep existing ones
999
 
1000
  product_to_edit['name'] = name
1001
  product_to_edit['price'] = price
@@ -1010,7 +991,6 @@ def admin():
1010
  except ValueError:
1011
  return "Ошибка: Неверный индекс товара.", 400
1012
  except Exception as e:
1013
- logging.error(f"Ошибка при редактировании товара: {e}")
1014
  return f"Ошибка при редактировании товара: {e}", 500
1015
 
1016
 
@@ -1018,9 +998,6 @@ def admin():
1018
  try:
1019
  index = int(request.form.get('index'))
1020
  if 0 <= index < len(products):
1021
- # Optional: Add logic here to delete photos from Hugging Face repo
1022
- # This would involve listing files in the 'photos' directory for this product
1023
- # and using api.delete_file
1024
  del products[index]
1025
  save_data(data)
1026
  return redirect(url_for('admin'))
@@ -1029,7 +1006,6 @@ def admin():
1029
  except ValueError:
1030
  return "Ошибка: Неверный индекс товара.", 400
1031
  except Exception as e:
1032
- logging.error(f"Ошибка при удалении товара: {e}")
1033
  return f"Ошибка при удалении товара: {e}", 500
1034
 
1035
  admin_html = '''
@@ -1101,7 +1077,7 @@ def admin():
1101
  border-radius: 8px;
1102
  font-size: 1rem;
1103
  transition: all 0.3s ease;
1104
- box-sizing: border-box; /* Include padding and border in element's total width */
1105
  }
1106
  input:focus, textarea:focus, select:focus {
1107
  border-color: var(--primary-color);
@@ -1408,19 +1384,14 @@ def download():
1408
  return f"Ошибка при скачивании базы данных: {e}", 500
1409
 
1410
  if __name__ == '__main__':
1411
- # Ensure the uploads directory exists
1412
  os.makedirs('uploads', exist_ok=True)
1413
 
1414
- # Start periodic backup thread
1415
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
1416
  backup_thread.start()
1417
 
1418
- # Attempt initial data load
1419
  try:
1420
  load_data()
1421
  except Exception as e:
1422
  logging.error(f"Не удалось выполнить начальную загрузку базы данных при запуске: {e}")
1423
- # Continue running, the app will likely start with empty data
1424
 
1425
- # Run the Flask app
1426
  app.run(debug=True, host='0.0.0.0', port=7860)
 
1
+ from flask import Flask, render_template_string, request, redirect, url_for
2
  import json
3
  import os
4
  import logging
 
27
  download_db_from_hf()
28
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
29
  data = json.load(file)
 
30
  if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
 
31
  if isinstance(data, list):
 
32
  return {'products': data, 'categories': []}
33
  else:
 
 
34
  return {'products': [], 'categories': []}
35
  return data
36
  except FileNotFoundError:
 
37
  return {'products': [], 'categories': []}
38
  except json.JSONDecodeError:
 
39
  return {'products': [], 'categories': []}
40
  except RepositoryNotFoundError:
 
41
  return {'products': [], 'categories': []}
42
  except HfHubHTTPError as e:
 
 
43
  try:
44
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
45
  data = json.load(file)
 
46
  if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
47
  if isinstance(data, list):
48
  return {'products': data, 'categories': []}
49
  else:
 
50
  return {'products': [], 'categories': []}
51
  return data
52
  except FileNotFoundError:
 
53
  return {'products': [], 'categories': []}
54
  except json.JSONDecodeError:
 
55
  return {'products': [], 'categories': []}
56
  except Exception as e:
 
57
  return {'products': [], 'categories': []}
58
 
59
  def save_data(data):
60
  try:
61
  with open(DATA_FILE, 'w', encoding='utf-8') as file:
62
  json.dump(data, file, ensure_ascii=False, indent=4)
 
63
  upload_db_to_hf()
64
  except Exception as e:
 
65
  raise
66
 
67
  def upload_db_to_hf():
68
  if not HF_TOKEN_WRITE:
 
69
  return
70
  try:
71
  api = HfApi()
 
77
  token=HF_TOKEN_WRITE,
78
  commit_message=f"Автоматическое резервное копирование базы данных {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
79
  )
 
80
  except Exception as e:
81
  logging.error(f"Ошибка при загрузке резервной копии на Hugging Face: {e}")
82
 
83
+
84
  def download_db_from_hf():
85
  if not HF_TOKEN_READ:
 
86
  return
87
  try:
88
  hf_hub_download(
 
93
  local_dir=".",
94
  local_dir_use_symlinks=False
95
  )
 
96
  except RepositoryNotFoundError:
97
+ raise
 
98
  except Exception as e:
99
+ raise
 
100
 
101
  def periodic_backup():
102
  while True:
 
213
  gap: 15px;
214
  padding: 10px;
215
  }
216
+ @media (max-width: 768px) {
217
+ .products-grid {
218
+ grid-template-columns: repeat(2, 1fr);
219
+ }
220
+ }
221
+ @media (max-width: 480px) {
222
+ .products-grid {
223
+ grid-template-columns: 1fr;
224
+ }
225
+ }
226
  .product {
227
  background: var(--light-text);
228
  border-radius: 15px;
 
285
  flex-grow: 1;
286
  }
287
  .product-buttons {
288
+ margin-top: auto;
289
  }
290
  .product-button {
291
  display: block;
 
342
  height: 100%;
343
  background-color: rgba(0,0,0,0.5);
344
  backdrop-filter: blur(5px);
345
+ overflow: auto;
346
  }
347
  .modal-content {
348
  background: var(--light-text);
 
415
 
416
  .quantity-input, .color-select {
417
  width: 100%;
418
+ max-width: 200px;
419
  padding: 8px;
420
  border: 1px solid var(--secondary-color);
421
  border-radius: 8px;
422
  font-size: 1rem;
423
  margin: 5px 0;
424
+ display: block;
425
  margin-bottom: 15px;
426
  }
427
  .modal-content button {
428
+ margin-top: 0;
429
  }
430
  .modal-content .product-button {
431
+ margin-top: 15px;
432
  }
433
 
434
  .clear-cart {
 
445
  background-color: #BA68C8;
446
  box-shadow: 0 4px 15px rgba(186, 104, 200, 0.4);
447
  }
448
+ #cart-count {
449
+ position: absolute;
450
+ top: -5px;
451
+ right: -5px;
452
+ background-color: red;
453
+ color: white;
454
+ border-radius: 50%;
455
+ padding: 2px 5px;
456
+ font-size: 0.7em;
457
+ display: none;
458
+ }
459
  @media (max-width: 768px) {
460
  .header {
461
  flex-direction: column;
 
465
  margin-right: 0;
466
  margin-bottom: 10px;
467
  }
 
 
 
468
  .modal-content {
469
  margin: 10% auto;
470
  }
 
474
  }
475
  @media (max-width: 480px) {
476
  .products-grid {
477
+ grid-template-columns: 1fr;
478
  }
479
  .modal-content {
480
  margin: 15% auto;
 
554
  </div>
555
  </div>
556
 
557
+ <button id="cart-button" onclick="openCartModal()">🛒<span id="cart-count">0</span></button>
558
 
559
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.js"></script>
560
  <script>
 
561
  const allProducts = {{ products|tojson }};
562
  let selectedProductName = null;
563
 
 
586
  new Swiper('.swiper-container', {
587
  slidesPerView: 1,
588
  spaceBetween: 20,
589
+ loop: true,
590
  grabCursor: true,
591
  pagination: { el: '.swiper-pagination', clickable: true },
592
  navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
 
634
 
635
  if (quantity <= 0) {
636
  alert("Укажите количество больше 0");
637
+ quantityInput.value = 1;
638
  return;
639
  }
640
 
 
677
  if (cart.length > 0) {
678
  cartButton.style.display = 'block';
679
  cartCount.textContent = totalItems;
680
+ cartCount.style.display = totalItems > 0 ? 'block' : 'none';
681
  } else {
682
  cartButton.style.display = 'none';
683
  cartCount.style.display = 'none';
 
731
  });
732
  orderText += `Итого к оплате: ${total} с`;
733
 
734
+ const phoneNumber = '996772179559';
735
  const whatsappUrl = `https://api.whatsapp.com/send?phone=${phoneNumber}&text=${encodeURIComponent(orderText)}`;
736
 
737
  window.open(whatsappUrl, '_blank');
738
+
 
739
  }
740
 
741
  function clearCart() {
 
770
  const category = productDiv.getAttribute('data-category');
771
  const matchesSearch = name.includes(searchTerm) || description.includes(searchTerm);
772
  const matchesCategory = activeCategory === 'all' || category === activeCategory;
773
+ productDiv.style.display = matchesSearch && matchesCategory ? 'flex' : 'none';
774
  });
775
  }
776
 
 
777
  updateCartButton();
778
+ filterProducts();
779
 
780
  </script>
781
  </body>
 
821
  @app.route('/')
822
  def catalog():
823
  data = load_data()
 
824
  products_for_display = sorted(data.get('products', []), key=lambda x: x.get('added_at', ''), reverse=True)
825
  categories = data.get('categories', [])
826
 
 
827
  return render_template_string(catalog_html, products=products_for_display, categories=categories, repo_id=REPO_ID, LOGO_URL=LOGO_URL)
828
 
829
  @app.route('/product_by_name/<string:product_name>')
830
  def product_detail_by_name(product_name):
831
  data = load_data()
 
832
  product = next((p for p in data.get('products', []) if p['name'] == product_name), None)
833
 
834
  if product:
 
858
  category_name_to_delete = request.form.get('category_name_to_delete')
859
  if category_name_to_delete in categories:
860
  categories.remove(category_name_to_delete)
 
861
  for product in products:
862
  if product.get('category') == category_name_to_delete:
863
  product['category'] = 'Без категории'
 
866
  else:
867
  return "Ошибка: Категория не найдена.", 400
868
  except Exception as e:
 
869
  return f"Ошибка при удалении категории: {e}", 500
870
 
871
  elif action == 'add':
 
888
  if photos_files:
889
  for photo in photos_files:
890
  if photo and photo.filename:
 
891
  original_filename = secure_filename(photo.filename)
 
892
  timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f")
893
  photo_filename = f"{timestamp}_{original_filename}"
894
 
 
898
  try:
899
  photo.save(temp_path)
900
  api = HfApi()
 
901
  api.upload_file(
902
  path_or_fileobj=temp_path,
903
  path_in_repo=f"photos/{photo_filename}",
 
909
  photos_list.append(photo_filename)
910
  except Exception as e:
911
  logging.error(f"Ошибка при загрузке фото {original_filename} на HF: {e}")
 
912
  pass
913
  finally:
 
914
  if os.path.exists(temp_path):
915
  os.remove(temp_path)
916
 
 
948
  if not name or price is None or not description:
949
  return "Ошибка: Заполните все обязательные поля для редактирования", 400
950
 
 
951
  if photos_files and any(photo.filename for photo in photos_files):
952
  new_photos_list = []
953
  for photo in photos_files:
 
977
  if os.path.exists(temp_path):
978
  os.remove(temp_path)
979
  product_to_edit['photos'] = new_photos_list
 
980
 
981
  product_to_edit['name'] = name
982
  product_to_edit['price'] = price
 
991
  except ValueError:
992
  return "Ошибка: Неверный индекс товара.", 400
993
  except Exception as e:
 
994
  return f"Ошибка при редактировании товара: {e}", 500
995
 
996
 
 
998
  try:
999
  index = int(request.form.get('index'))
1000
  if 0 <= index < len(products):
 
 
 
1001
  del products[index]
1002
  save_data(data)
1003
  return redirect(url_for('admin'))
 
1006
  except ValueError:
1007
  return "Ошибка: Неверный индекс товара.", 400
1008
  except Exception as e:
 
1009
  return f"Ошибка при удалении товара: {e}", 500
1010
 
1011
  admin_html = '''
 
1077
  border-radius: 8px;
1078
  font-size: 1rem;
1079
  transition: all 0.3s ease;
1080
+ box-sizing: border-box;
1081
  }
1082
  input:focus, textarea:focus, select:focus {
1083
  border-color: var(--primary-color);
 
1384
  return f"Ошибка при скачивании базы данных: {e}", 500
1385
 
1386
  if __name__ == '__main__':
 
1387
  os.makedirs('uploads', exist_ok=True)
1388
 
 
1389
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
1390
  backup_thread.start()
1391
 
 
1392
  try:
1393
  load_data()
1394
  except Exception as e:
1395
  logging.error(f"Не удалось выполнить начальную загрузку базы данных при запуске: {e}")
 
1396
 
 
1397
  app.run(debug=True, host='0.0.0.0', port=7860)