Kgshop commited on
Commit
3ccd3f5
·
verified ·
1 Parent(s): 9a91292

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -32
app.py CHANGED
@@ -84,38 +84,45 @@ def download_db_from_hf(specific_file=None, retries=DOWNLOAD_RETRIES, delay=DOWN
84
  force_download=True,
85
  resume_download=False
86
  )
 
87
  success = True
88
  break
89
  except RepositoryNotFoundError:
 
90
  return False
91
  except HfHubHTTPError as e:
92
  if e.response.status_code == 404:
 
93
  if attempt == 0 and not os.path.exists(file_name):
94
  try:
95
  if file_name == DATA_FILE:
96
  with open(file_name, 'w', encoding='utf-8') as f:
97
  json.dump({}, f)
 
98
  except Exception as create_e:
99
- pass
100
  success = False
101
  break
102
  else:
103
- pass
104
  except requests.exceptions.RequestException as e:
105
- pass
106
  except Exception as e:
107
- pass
108
 
109
  if attempt < retries:
 
110
  time.sleep(delay)
111
 
112
  if not success:
 
113
  all_successful = False
114
 
115
  return all_successful
116
 
117
  def upload_db_to_hf(specific_file=None):
118
  if not HF_TOKEN_WRITE:
 
119
  return
120
  try:
121
  api = HfApi()
@@ -132,17 +139,19 @@ def upload_db_to_hf(specific_file=None):
132
  token=HF_TOKEN_WRITE,
133
  commit_message=f"Sync {file_name} {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
134
  )
 
135
  except Exception as e:
136
- pass
137
  else:
138
- pass
139
  except Exception as e:
140
- pass
141
 
142
  def periodic_backup():
143
  backup_interval = 1800
144
  while True:
145
  time.sleep(backup_interval)
 
146
  upload_db_to_hf()
147
 
148
  def load_data():
@@ -150,15 +159,19 @@ def load_data():
150
  with open(DATA_FILE, 'r', encoding='utf-8') as f:
151
  data = json.load(f)
152
  if not isinstance(data, dict):
 
153
  data = {}
154
  except (FileNotFoundError, json.JSONDecodeError):
 
155
  if download_db_from_hf(specific_file=DATA_FILE):
156
  try:
157
  with open(DATA_FILE, 'r', encoding='utf-8') as f:
158
  data = json.load(f)
159
  if not isinstance(data, dict):
 
160
  data = {}
161
  except (FileNotFoundError, json.JSONDecodeError):
 
162
  data = {}
163
  else:
164
  data = {}
@@ -168,9 +181,11 @@ def save_data(data):
168
  try:
169
  with open(DATA_FILE, 'w', encoding='utf-8') as file:
170
  json.dump(data, file, ensure_ascii=False, indent=4)
171
- upload_db_to_hf(specific_file=DATA_FILE)
 
 
172
  except Exception as e:
173
- pass
174
 
175
  def is_chat_active(env_id):
176
  data = get_env_data(env_id)
@@ -251,11 +266,14 @@ def save_env_data(env_id, env_data):
251
 
252
  def configure_gemini():
253
  if not GOOGLE_API_KEY:
 
254
  return False
255
  try:
256
  genai.configure(api_key=GOOGLE_API_KEY)
 
257
  return True
258
  except Exception as e:
 
259
  return False
260
 
261
  def generate_ai_description_from_image(image_data, language):
@@ -268,6 +286,7 @@ def generate_ai_description_from_image(image_data, language):
268
  image_stream = io.BytesIO(image_data)
269
  image = Image.open(image_stream).convert('RGB')
270
  except Exception as e:
 
271
  raise ValueError(f"Не удалось обработать изображение. Убедитесь, что это действительный файл изображения.")
272
 
273
  base_prompt = "Напиши большой и красивый, содержательный рекламный пост минимум на 1000 символов со смайликами и 25 тематических хэштегов с ключевыми словами разных вариантов, чтобы мои клиенты могли найти меня в поиске Instagram, Google и т.д. по ключевым словам. Пост пиши исключительно под товар, который на фото, без адресов и номеров телефона."
@@ -299,6 +318,7 @@ def generate_ai_description_from_image(image_data, language):
299
  return response.text
300
 
301
  except Exception as e:
 
302
  if "API key not valid" in str(e):
303
  raise ValueError("Внутренняя ошибка конфигурации API.")
304
  elif " Billing account not found" in str(e):
@@ -404,6 +424,7 @@ def generate_chat_response(message, chat_history_from_client, env_id):
404
  return generated_text
405
 
406
  except Exception as e:
 
407
  if "API key not valid" in str(e):
408
  return "Внутренняя ошибка конфигурации API."
409
  elif " Billing account not found" in str(e):
@@ -2055,11 +2076,13 @@ ADMIN_TEMPLATE = '''
2055
  .item .description { font-size: 0.85rem; color: #999; max-height: 60px; overflow: hidden; text-overflow: ellipsis; }
2056
  .item-actions { margin-top: 15px; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
2057
  .edit-form-container { margin-top: 15px; padding: 20px; background: #E0F2F1; border: 1px dashed #B2DFDB; border-radius: 6px; display: none; }
2058
- details { background-color: #fdfdff; border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 20px; }
2059
- details > summary { cursor: pointer; font-weight: 600; color: var(--bg-medium); display: block; padding: 15px; border-bottom: 1px solid #e0e0e0; list-style: none; position: relative; }
 
 
 
2060
  details > summary::after { content: '\\f078'; font-family: 'Font Awesome 6 Free'; font-weight: 900; position: absolute; right: 20px; top: 50%; transform: translateY(-50%); transition: transform 0.2s ease; color: var(--bg-medium); }
2061
  details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
2062
- details[open] > summary { border-bottom: 1px solid #e0e0e0; }
2063
  details .form-content { padding: 20px; }
2064
  .color-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
2065
  .color-input-group input { flex-grow: 1; margin: 0; }
@@ -2121,7 +2144,8 @@ ADMIN_TEMPLATE = '''
2121
  <p>Чат <strong>активен</strong>. Срок действия истекает: <strong class="{{ 'status-indicator expires-soon' if chat_status.expires_soon else '' }}">{{ chat_status.expires_date }}</strong></p>
2122
  {% else %}
2123
  <p>Чат <strong>неактивен</strong>. {% if chat_status.expires_date %}Срок действия истек: {{ chat_status.expires_date }}{% endif %}</p>
2124
- <p style="color: var(--danger);">Для возобновления работы чат-бота, активируйте его в <a href="{{ url_for('admhosto') }}">главной админ-панели</a>.</p>
 
2125
  {% endif %}
2126
  </div>
2127
 
@@ -2183,20 +2207,22 @@ ADMIN_TEMPLATE = '''
2183
 
2184
 
2185
  <div class="section">
2186
- <h2><i class="fas fa-comments"></i> Диалоги с {{ settings.chat_name }}</h2>
2187
- <div class="item-list">
2188
- {% if chats %}
2189
- {% for chat_id, chat_data in chats.items()|sort(reverse=True) %}
2190
- <div class="chat-log-item" onclick="viewChat('{{ chat_id }}')">
2191
- <strong>ID Диалога:</strong> {{ chat_id }}
2192
- <br>
2193
- <small>Сообщений: {{ chat_data|length }} | Последнее сообщение: {{ chat_data[-1].timestamp if chat_data and 'timestamp' in chat_data[-1] else 'N/A' }}</small>
2194
- </div>
2195
- {% endfor %}
2196
- {% else %}
2197
- <p>Пока не было ни одного диалога.</p>
2198
- {% endif %}
2199
- </div>
 
 
2200
  </div>
2201
 
2202
  <div class="flex-container">
@@ -2820,6 +2846,7 @@ def create_order(env_id):
2820
  })
2821
  total_price += price * quantity
2822
  except (ValueError, TypeError) as e:
 
2823
  return jsonify({"error": "Неверная цена или количество в товаре."}), 400
2824
 
2825
  order_id = f"{datetime.now().strftime('%Y%m%d%H%M%S')}-{uuid4().hex[:6]}"
@@ -2844,6 +2871,7 @@ def create_order(env_id):
2844
  return jsonify({"order_id": order_id}), 201
2845
 
2846
  except Exception as e:
 
2847
  return jsonify({"error": "Ошибка сервера при сохранении заказа."}), 500
2848
 
2849
  @app.route('/<env_id>/order/<order_id>')
@@ -2931,8 +2959,8 @@ def admin(env_id):
2931
  if old_avatar:
2932
  try:
2933
  api.delete_files(repo_id=REPO_ID, paths_in_repo=[f"avatars/{old_avatar}"], repo_type="dataset", token=HF_TOKEN_WRITE)
2934
- except Exception:
2935
- pass
2936
 
2937
  ext = os.path.splitext(avatar_file.filename)[1].lower()
2938
  avatar_filename = f"avatar_{env_id}_{int(time.time())}{ext}"
@@ -2952,6 +2980,7 @@ def admin(env_id):
2952
  flash("Аватар чата успешно обновлен.", 'success')
2953
  except Exception as e:
2954
  flash(f"Ошибка при загрузке аватара: {e}", 'error')
 
2955
  else:
2956
  flash("HF_TOKEN (write) не настроен. Аватар не был загружен.", "warning")
2957
 
@@ -3015,6 +3044,7 @@ def admin(env_id):
3015
  uploaded_count += 1
3016
  except Exception as e:
3017
  flash(f"Ошибка при загрузке фото {photo.filename}.", 'error')
 
3018
  if os.path.exists(temp_path):
3019
  try: os.remove(temp_path)
3020
  except OSError: pass
@@ -3024,7 +3054,7 @@ def admin(env_id):
3024
  if os.path.exists(uploads_dir) and not os.listdir(uploads_dir):
3025
  os.rmdir(uploads_dir)
3026
  except OSError as e:
3027
- pass
3028
  elif not HF_TOKEN_WRITE and photos_files and any(f.filename for f in photos_files):
3029
  flash("HF_TOKEN (write) не настроен. Фотографии не были загружены.", "warning")
3030
 
@@ -3098,6 +3128,7 @@ def admin(env_id):
3098
  uploaded_count += 1
3099
  except Exception as e:
3100
  flash(f"Ошибка при загрузке нового фото {photo.filename}.", 'error')
 
3101
  if os.path.exists(temp_path):
3102
  try: os.remove(temp_path)
3103
  except OSError: pass
@@ -3105,7 +3136,7 @@ def admin(env_id):
3105
  if os.path.exists(uploads_dir) and not os.listdir(uploads_dir):
3106
  os.rmdir(uploads_dir)
3107
  except OSError as e:
3108
- pass
3109
 
3110
  if new_photos_list:
3111
  old_photos = product_to_edit.get('photos', [])
@@ -3121,6 +3152,7 @@ def admin(env_id):
3121
  )
3122
  except Exception as e:
3123
  flash("Не удалось удалить старые фотографии с сервера. Новые фото загружены.", "warning")
 
3124
  product_to_edit['photos'] = new_photos_list
3125
  flash("Фотографии товара успешно обновлены.", "success")
3126
  elif uploaded_count == 0 and any(f.filename for f in photos_files):
@@ -3155,6 +3187,7 @@ def admin(env_id):
3155
  )
3156
  except Exception as e:
3157
  flash(f"Не удалось удалить фото для товара '{product_name}' с сервера. Товар удален локально.", "warning")
 
3158
  elif photos_to_delete and not HF_TOKEN_WRITE:
3159
  flash(f"Товар '{product_name}' удален локально, но фото не удалены с сервера (токен не задан).", "warning")
3160
 
@@ -3168,6 +3201,7 @@ def admin(env_id):
3168
  return redirect(url_for('admin', env_id=env_id))
3169
 
3170
  except Exception as e:
 
3171
  flash(f"Произошла внутренняя ошибка при выполнении действия '{action}'. Подробности в логе сервера.", 'error')
3172
  return redirect(url_for('admin', env_id=env_id))
3173
 
@@ -3228,6 +3262,7 @@ def handle_generate_description_ai():
3228
  except ValueError as ve:
3229
  return jsonify({"error": str(ve)}), 400
3230
  except Exception as e:
 
3231
  return jsonify({"error": f"Внутренняя ошибка сервера: {e}"}), 500
3232
 
3233
  @app.route('/<env_id>/chat_with_ai', methods=['POST'])
@@ -3261,6 +3296,7 @@ def handle_chat_with_ai(env_id):
3261
 
3262
  return jsonify({"text": ai_response_text})
3263
  except Exception as e:
 
3264
  return jsonify({"error": f"Ошибка чата: {e}"}), 500
3265
 
3266
  @app.route('/<env_id>/chat')
@@ -3302,6 +3338,7 @@ def force_upload(env_id):
3302
  flash("Данные успешно загружены на Hugging Face.", 'success')
3303
  except Exception as e:
3304
  flash(f"Ошибка при загрузке на Hugging Face: {e}", 'error')
 
3305
  return redirect(url_for('admin', env_id=env_id))
3306
 
3307
  @app.route('/<env_id>/force_download', methods=['POST'])
@@ -3313,6 +3350,7 @@ def force_download(env_id):
3313
  flash("Не удалось скачать данные с Hugging Face после нескольких попыток. Проверьте логи.", 'error')
3314
  except Exception as e:
3315
  flash(f"Ошибка при скачивании с Hugging Face: {e}", 'error')
 
3316
  return redirect(url_for('admin', env_id=env_id))
3317
 
3318
  if __name__ == '__main__':
@@ -3323,8 +3361,9 @@ if __name__ == '__main__':
3323
  if HF_TOKEN_WRITE:
3324
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
3325
  backup_thread.start()
 
3326
  else:
3327
- pass
3328
 
3329
  port = int(os.environ.get('PORT', 7860))
3330
  app.run(debug=False, host='0.0.0.0', port=port)
 
84
  force_download=True,
85
  resume_download=False
86
  )
87
+ logging.info(f"Successfully downloaded {file_name} to {local_path}")
88
  success = True
89
  break
90
  except RepositoryNotFoundError:
91
+ logging.error(f"Repository {REPO_ID} not found. Cannot download file.")
92
  return False
93
  except HfHubHTTPError as e:
94
  if e.response.status_code == 404:
95
+ logging.warning(f"{file_name} not found in repo. Will try to create a local empty one if needed.")
96
  if attempt == 0 and not os.path.exists(file_name):
97
  try:
98
  if file_name == DATA_FILE:
99
  with open(file_name, 'w', encoding='utf-8') as f:
100
  json.dump({}, f)
101
+ logging.info(f"Created empty local file: {file_name}")
102
  except Exception as create_e:
103
+ logging.error(f"Could not create local empty file {file_name}: {create_e}")
104
  success = False
105
  break
106
  else:
107
+ logging.error(f"HTTP error downloading {file_name} on attempt {attempt+1}: {e}")
108
  except requests.exceptions.RequestException as e:
109
+ logging.error(f"Network error downloading {file_name} on attempt {attempt+1}: {e}")
110
  except Exception as e:
111
+ logging.error(f"An unexpected error occurred downloading {file_name} on attempt {attempt+1}: {e}")
112
 
113
  if attempt < retries:
114
+ logging.info(f"Retrying download of {file_name} in {delay} seconds...")
115
  time.sleep(delay)
116
 
117
  if not success:
118
+ logging.error(f"Failed to download {file_name} after {retries+1} attempts.")
119
  all_successful = False
120
 
121
  return all_successful
122
 
123
  def upload_db_to_hf(specific_file=None):
124
  if not HF_TOKEN_WRITE:
125
+ logging.warning("HF_TOKEN_WRITE not set. Skipping upload to Hugging Face.")
126
  return
127
  try:
128
  api = HfApi()
 
139
  token=HF_TOKEN_WRITE,
140
  commit_message=f"Sync {file_name} {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
141
  )
142
+ logging.info(f"Successfully uploaded {file_name} to Hugging Face.")
143
  except Exception as e:
144
+ logging.error(f"Failed to upload {file_name} to Hugging Face: {e}")
145
  else:
146
+ logging.warning(f"File {file_name} not found locally. Skipping upload.")
147
  except Exception as e:
148
+ logging.error(f"An unexpected error occurred during the upload process: {e}")
149
 
150
  def periodic_backup():
151
  backup_interval = 1800
152
  while True:
153
  time.sleep(backup_interval)
154
+ logging.info("Starting periodic backup...")
155
  upload_db_to_hf()
156
 
157
  def load_data():
 
159
  with open(DATA_FILE, 'r', encoding='utf-8') as f:
160
  data = json.load(f)
161
  if not isinstance(data, dict):
162
+ logging.warning(f"{DATA_FILE} does not contain a dictionary. Resetting to empty.")
163
  data = {}
164
  except (FileNotFoundError, json.JSONDecodeError):
165
+ logging.warning(f"{DATA_FILE} not found or corrupted. Attempting to download from Hugging Face.")
166
  if download_db_from_hf(specific_file=DATA_FILE):
167
  try:
168
  with open(DATA_FILE, 'r', encoding='utf-8') as f:
169
  data = json.load(f)
170
  if not isinstance(data, dict):
171
+ logging.warning(f"Downloaded {DATA_FILE} is not a dictionary. Resetting to empty.")
172
  data = {}
173
  except (FileNotFoundError, json.JSONDecodeError):
174
+ logging.error(f"Failed to load {DATA_FILE} even after download. Using empty data.")
175
  data = {}
176
  else:
177
  data = {}
 
181
  try:
182
  with open(DATA_FILE, 'w', encoding='utf-8') as file:
183
  json.dump(data, file, ensure_ascii=False, indent=4)
184
+ upload_thread = threading.Thread(target=upload_db_to_hf, kwargs={'specific_file': DATA_FILE})
185
+ upload_thread.daemon = True
186
+ upload_thread.start()
187
  except Exception as e:
188
+ logging.error(f"Error in save_data: {e}")
189
 
190
  def is_chat_active(env_id):
191
  data = get_env_data(env_id)
 
266
 
267
  def configure_gemini():
268
  if not GOOGLE_API_KEY:
269
+ logging.warning("GOOGLE_API_KEY is not set. Gemini AI features will be disabled.")
270
  return False
271
  try:
272
  genai.configure(api_key=GOOGLE_API_KEY)
273
+ logging.info("Google Gemini AI configured successfully.")
274
  return True
275
  except Exception as e:
276
+ logging.error(f"Failed to configure Google Gemini AI: {e}")
277
  return False
278
 
279
  def generate_ai_description_from_image(image_data, language):
 
286
  image_stream = io.BytesIO(image_data)
287
  image = Image.open(image_stream).convert('RGB')
288
  except Exception as e:
289
+ logging.error(f"Error processing image for AI description: {e}")
290
  raise ValueError(f"Не удалось обработать изображение. Убедитесь, что это действительный файл изображения.")
291
 
292
  base_prompt = "Напиши большой и красивый, содержательный рекламный пост минимум на 1000 символов со смайликами и 25 тематических хэштегов с ключевыми словами разных вариантов, чтобы мои клиенты могли найти меня в поиске Instagram, Google и т.д. по ключевым словам. Пост пиши исключительно под товар, который на фото, без адресов и номеров телефона."
 
318
  return response.text
319
 
320
  except Exception as e:
321
+ logging.error(f"Error during AI content generation: {e}")
322
  if "API key not valid" in str(e):
323
  raise ValueError("Внутренняя ошибка конфигурации API.")
324
  elif " Billing account not found" in str(e):
 
424
  return generated_text
425
 
426
  except Exception as e:
427
+ logging.error(f"Error generating chat response: {e}")
428
  if "API key not valid" in str(e):
429
  return "Внутренняя ошибка конфигурации API."
430
  elif " Billing account not found" in str(e):
 
2076
  .item .description { font-size: 0.85rem; color: #999; max-height: 60px; overflow: hidden; text-overflow: ellipsis; }
2077
  .item-actions { margin-top: 15px; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
2078
  .edit-form-container { margin-top: 15px; padding: 20px; background: #E0F2F1; border: 1px dashed #B2DFDB; border-radius: 6px; display: none; }
2079
+ details { background-color: #fdfdff; border-radius: 8px; margin-bottom: 10px; }
2080
+ details:not(.section) { border: 1px solid #e0e0e0; }
2081
+ details > summary { cursor: pointer; font-weight: 600; color: var(--bg-medium); display: block; padding: 15px; list-style: none; position: relative; }
2082
+ details:not(.section) > summary { border-bottom: 1px solid #e0e0e0; }
2083
+ details[open] > summary { border-bottom: 1px solid #e0e0e0; }
2084
  details > summary::after { content: '\\f078'; font-family: 'Font Awesome 6 Free'; font-weight: 900; position: absolute; right: 20px; top: 50%; transform: translateY(-50%); transition: transform 0.2s ease; color: var(--bg-medium); }
2085
  details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
 
2086
  details .form-content { padding: 20px; }
2087
  .color-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
2088
  .color-input-group input { flex-grow: 1; margin: 0; }
 
2144
  <p>Чат <strong>активен</strong>. Срок действия истекает: <strong class="{{ 'status-indicator expires-soon' if chat_status.expires_soon else '' }}">{{ chat_status.expires_date }}</strong></p>
2145
  {% else %}
2146
  <p>Чат <strong>неактивен</strong>. {% if chat_status.expires_date %}Срок действия истек: {{ chat_status.expires_date }}{% endif %}</p>
2147
+ <p>Для активации или продления чат-бота, пожалуйста, свяжитесь с администратором.</p>
2148
+ <a href="https://wa.me/77472479197" class="button add-button" target="_blank" style="margin-top: 10px;"><i class="fab fa-whatsapp"></i> Написать администратору</a>
2149
  {% endif %}
2150
  </div>
2151
 
 
2207
 
2208
 
2209
  <div class="section">
2210
+ <details>
2211
+ <summary><h2><i class="fas fa-comments"></i> Диалоги с {{ settings.chat_name }}</h2></summary>
2212
+ <div class="item-list" style="margin-top: 20px;">
2213
+ {% if chats %}
2214
+ {% for chat_id, chat_data in chats.items()|sort(reverse=True) %}
2215
+ <div class="chat-log-item" onclick="viewChat('{{ chat_id }}')">
2216
+ <strong>ID Диалога:</strong> {{ chat_id }}
2217
+ <br>
2218
+ <small>Сообщений: {{ chat_data|length }} | Последнее сообщение: {{ chat_data[-1].timestamp if chat_data and 'timestamp' in chat_data[-1] else 'N/A' }}</small>
2219
+ </div>
2220
+ {% endfor %}
2221
+ {% else %}
2222
+ <p>Пока не было ни одного диалога.</p>
2223
+ {% endif %}
2224
+ </div>
2225
+ </details>
2226
  </div>
2227
 
2228
  <div class="flex-container">
 
2846
  })
2847
  total_price += price * quantity
2848
  except (ValueError, TypeError) as e:
2849
+ logging.error(f"Invalid cart item format: {e}")
2850
  return jsonify({"error": "Неверная цена или количество в товаре."}), 400
2851
 
2852
  order_id = f"{datetime.now().strftime('%Y%m%d%H%M%S')}-{uuid4().hex[:6]}"
 
2871
  return jsonify({"order_id": order_id}), 201
2872
 
2873
  except Exception as e:
2874
+ logging.error(f"Server error while creating order: {e}")
2875
  return jsonify({"error": "Ошибка сервера при сохранении заказа."}), 500
2876
 
2877
  @app.route('/<env_id>/order/<order_id>')
 
2959
  if old_avatar:
2960
  try:
2961
  api.delete_files(repo_id=REPO_ID, paths_in_repo=[f"avatars/{old_avatar}"], repo_type="dataset", token=HF_TOKEN_WRITE)
2962
+ except Exception as e:
2963
+ logging.warning(f"Could not delete old avatar {old_avatar}: {e}")
2964
 
2965
  ext = os.path.splitext(avatar_file.filename)[1].lower()
2966
  avatar_filename = f"avatar_{env_id}_{int(time.time())}{ext}"
 
2980
  flash("Аватар чата успешно обновлен.", 'success')
2981
  except Exception as e:
2982
  flash(f"Ошибка при загрузке аватара: {e}", 'error')
2983
+ logging.error(f"Error uploading avatar: {e}")
2984
  else:
2985
  flash("HF_TOKEN (write) не настроен. Аватар не был загружен.", "warning")
2986
 
 
3044
  uploaded_count += 1
3045
  except Exception as e:
3046
  flash(f"Ошибка при загрузке фото {photo.filename}.", 'error')
3047
+ logging.error(f"Error uploading photo {photo.filename}: {e}")
3048
  if os.path.exists(temp_path):
3049
  try: os.remove(temp_path)
3050
  except OSError: pass
 
3054
  if os.path.exists(uploads_dir) and not os.listdir(uploads_dir):
3055
  os.rmdir(uploads_dir)
3056
  except OSError as e:
3057
+ logging.warning(f"Could not remove temp upload directory: {e}")
3058
  elif not HF_TOKEN_WRITE and photos_files and any(f.filename for f in photos_files):
3059
  flash("HF_TOKEN (write) не настроен. Фотографии не были загружены.", "warning")
3060
 
 
3128
  uploaded_count += 1
3129
  except Exception as e:
3130
  flash(f"Ошибка при загрузке нового фото {photo.filename}.", 'error')
3131
+ logging.error(f"Error uploading new photo {photo.filename}: {e}")
3132
  if os.path.exists(temp_path):
3133
  try: os.remove(temp_path)
3134
  except OSError: pass
 
3136
  if os.path.exists(uploads_dir) and not os.listdir(uploads_dir):
3137
  os.rmdir(uploads_dir)
3138
  except OSError as e:
3139
+ logging.warning(f"Could not remove temp upload directory: {e}")
3140
 
3141
  if new_photos_list:
3142
  old_photos = product_to_edit.get('photos', [])
 
3152
  )
3153
  except Exception as e:
3154
  flash("Не удалось удалить старые фотографии с сервера. Новые фото загружены.", "warning")
3155
+ logging.warning(f"Could not delete old photos for product {product_to_edit['name']}: {e}")
3156
  product_to_edit['photos'] = new_photos_list
3157
  flash("Фотографии товара успешно обновлены.", "success")
3158
  elif uploaded_count == 0 and any(f.filename for f in photos_files):
 
3187
  )
3188
  except Exception as e:
3189
  flash(f"Не удалось удалить фото для товара '{product_name}' с сервера. Товар удален локально.", "warning")
3190
+ logging.warning(f"Could not delete photos for product {product_name}: {e}")
3191
  elif photos_to_delete and not HF_TOKEN_WRITE:
3192
  flash(f"Товар '{product_name}' удален локально, но фото не удалены с сервера (токен не задан).", "warning")
3193
 
 
3201
  return redirect(url_for('admin', env_id=env_id))
3202
 
3203
  except Exception as e:
3204
+ logging.error(f"An internal error occurred in admin POST action '{action}': {e}")
3205
  flash(f"Произошла внутренняя ошибка при выполнении действия '{action}'. Подробности в логе сервера.", 'error')
3206
  return redirect(url_for('admin', env_id=env_id))
3207
 
 
3262
  except ValueError as ve:
3263
  return jsonify({"error": str(ve)}), 400
3264
  except Exception as e:
3265
+ logging.error(f"Internal server error during AI description generation: {e}")
3266
  return jsonify({"error": f"Внутренняя ошибка сервера: {e}"}), 500
3267
 
3268
  @app.route('/<env_id>/chat_with_ai', methods=['POST'])
 
3296
 
3297
  return jsonify({"text": ai_response_text})
3298
  except Exception as e:
3299
+ logging.error(f"Error in chat handler: {e}")
3300
  return jsonify({"error": f"Ошибка чата: {e}"}), 500
3301
 
3302
  @app.route('/<env_id>/chat')
 
3338
  flash("Данные успешно загружены на Hugging Face.", 'success')
3339
  except Exception as e:
3340
  flash(f"Ошибка при загрузке на Hugging Face: {e}", 'error')
3341
+ logging.error(f"Error during forced upload: {e}")
3342
  return redirect(url_for('admin', env_id=env_id))
3343
 
3344
  @app.route('/<env_id>/force_download', methods=['POST'])
 
3350
  flash("Не удалось скачать данные с Hugging Face после нескольких попыток. Проверьте логи.", 'error')
3351
  except Exception as e:
3352
  flash(f"Ошибка при скачивании с Hugging Face: {e}", 'error')
3353
+ logging.error(f"Error during forced download: {e}")
3354
  return redirect(url_for('admin', env_id=env_id))
3355
 
3356
  if __name__ == '__main__':
 
3361
  if HF_TOKEN_WRITE:
3362
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
3363
  backup_thread.start()
3364
+ logging.info("Periodic backup thread started.")
3365
  else:
3366
+ logging.warning("HF_TOKEN_WRITE not set, periodic backups are disabled.")
3367
 
3368
  port = int(os.environ.get('PORT', 7860))
3369
  app.run(debug=False, host='0.0.0.0', port=port)