Kgshop commited on
Commit
54a9e0a
·
verified ·
1 Parent(s): 05072df

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +38 -54
app.py CHANGED
@@ -23,7 +23,8 @@ app.secret_key = os.getenv("FLASK_SECRET_KEY", 'your_unique_secret_key_korea_glo
23
  app.config.update(
24
  SESSION_COOKIE_SAMESITE="None",
25
  SESSION_COOKIE_SECURE=True,
26
- SESSION_COOKIE_HTTPONLY=True
 
27
  )
28
 
29
  DATA_FILE = 'data_kglobal.json'
@@ -42,7 +43,6 @@ CURRENCY_NAME = 'Доллар США ($)'
42
  DOWNLOAD_RETRIES = 3
43
  DOWNLOAD_DELAY = 5
44
  UPLOAD_DELAY = 2
45
- BACKUP_INTERVAL = 1800
46
 
47
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
48
 
@@ -137,9 +137,6 @@ def _load_from_file(file_path, default_value, lock):
137
  if 'orders' not in content: content['orders'] = {}
138
  elif file_path == USERS_FILE:
139
  if not isinstance(content, dict): raise ValueError("Users file is not a dictionary")
140
-
141
-
142
- pass
143
  return content
144
  except (FileNotFoundError, json.JSONDecodeError, ValueError) as e:
145
  logging.error(f"Error loading local file {file_path}: {e}. Returning default.")
@@ -183,7 +180,9 @@ def save_data(new_data):
183
  with open(DATA_FILE, 'w', encoding='utf-8') as file:
184
  json.dump(app_data, file, ensure_ascii=False, indent=4)
185
  logging.info(f"Data successfully saved to {DATA_FILE} and memory cache updated.")
186
- return True
 
 
187
  except Exception as e:
188
  logging.error(f"Error saving data to {DATA_FILE}: {e}", exc_info=True)
189
  return False
@@ -203,7 +202,9 @@ def save_users(new_users):
203
  with open(USERS_FILE, 'w', encoding='utf-8') as file:
204
  json.dump(app_users, file, ensure_ascii=False, indent=4)
205
  logging.info(f"User data successfully saved to {USERS_FILE} and memory cache updated.")
206
- return True
 
 
207
  except Exception as e:
208
  logging.error(f"Error saving user data to {USERS_FILE}: {e}", exc_info=True)
209
  return False
@@ -221,16 +222,14 @@ def upload_db_to_hf(specific_file=None):
221
  for file_name in files_to_upload:
222
  if os.path.exists(file_name):
223
  try:
224
- lock = data_lock if file_name == DATA_FILE else users_lock
225
- with lock:
226
- api.upload_file(
227
- path_or_fileobj=file_name,
228
- path_in_repo=file_name,
229
- repo_id=REPO_ID,
230
- repo_type="dataset",
231
- token=HF_TOKEN_WRITE,
232
- commit_message=f"Sync {file_name} {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
233
- )
234
  logging.info(f"File {file_name} successfully uploaded to Hugging Face.")
235
  time.sleep(UPLOAD_DELAY)
236
  except Exception as e:
@@ -246,21 +245,6 @@ def upload_db_to_hf(specific_file=None):
246
  logging.error(f"General error during Hugging Face upload initialization or process: {e}", exc_info=True)
247
  return False
248
 
249
- def periodic_backup():
250
- logging.info(f"Setting up periodic backup every {BACKUP_INTERVAL} seconds.")
251
- while True:
252
- time.sleep(BACKUP_INTERVAL)
253
- logging.info("Starting periodic backup...")
254
- try:
255
- upload_success = upload_db_to_hf()
256
- if upload_success:
257
- logging.info("Periodic backup finished successfully.")
258
- else:
259
- logging.warning("Periodic backup finished with errors (some files might not have been uploaded).")
260
- except Exception as e:
261
- logging.error(f"Error during periodic backup execution: {e}", exc_info=True)
262
-
263
-
264
  CATALOG_TEMPLATE = '''
265
  <!DOCTYPE html>
266
  <html lang="ru">
@@ -344,7 +328,7 @@ CATALOG_TEMPLATE = '''
344
  .cart-item-price { font-size: 0.9rem; color: #444c5a; }
345
  body.dark-mode .cart-item-price { color: #8aa9b5; }
346
  .cart-item-total { font-weight: bold; text-align: right; grid-column: 3; font-size: 1rem; }
347
- .cart-item-remove { grid-column: 4; background:none; border:none; color:#f56565; cursor:pointer; font-size: 1.3em; padding: 5px; line-height: 1; }
348
  .cart-item-remove:hover { color: #c53030; }
349
  .quantity-input, .color-select { width: 100%; max-width: 180px; padding: 10px; border: 1px solid #d4d9e3; border-radius: 8px; font-size: 1rem; margin: 10px 0; box-sizing: border-box; }
350
  body.dark-mode .quantity-input, body.dark-mode .color-select { background-color: #1a114f; border-color: #102a50; color: #c8d0e0; }
@@ -1117,8 +1101,8 @@ ADMIN_TEMPLATE = '''
1117
  <button type="submit" class="button download-hf-button" title="Скачать файлы (перезапишет локальные)"><i class="fas fa-download"></i> Скачать БД</button>
1118
  </form>
1119
  </div>
1120
- <p style="font-size: 0.85rem; color: #5e6e75;">Резервное копирование происходит автоматически каждые {{ backup_interval // 60 }} минут. Используйте эти кнопки для немедленной синхронизации.</p>
1121
- <p style="font-size: 0.85rem; color: #5e6e75;">Сохранение данных (товары, пользователи, категории) происходит только локально, синхронизация с датацентром - по расписанию или принудительно.</p>
1122
  </div>
1123
 
1124
 
@@ -1703,9 +1687,13 @@ def login():
1703
  localStorage.setItem('kglobalUserToken', '{token_attempt}');
1704
  console.log('Stored token in localStorage.');
1705
  }} catch (e) {{ console.error("Error saving token to localStorage:", e); }}
1706
- window.location.href = "{url_for('catalog')}";
 
 
 
 
1707
  </script>
1708
- <p>Вход выполнен успешно. Перенаправление в <a href="{url_for('catalog')}">каталог</a>...</p>
1709
  </body></html>
1710
  '''
1711
  return login_response_html
@@ -1758,9 +1746,13 @@ def logout():
1758
  localStorage.removeItem('kglobalUserToken');
1759
  console.log('Removed stored token from localStorage.');
1760
  }} catch (e) {{ console.error("Error removing from localStorage:", e); }}
1761
- window.location.href = "{url_for('catalog')}";
 
 
 
 
1762
  </script>
1763
- <p>Выход выполнен. Перенаправление на <a href="{url_for('catalog')}">главную страницу</a>...</p>
1764
  </body></html>
1765
  '''
1766
  return logout_response_html
@@ -2275,8 +2267,7 @@ def admin():
2275
  categories=display_categories,
2276
  users=display_users_sorted_dict,
2277
  repo_id=REPO_ID,
2278
- currency_code=CURRENCY_CODE,
2279
- backup_interval=BACKUP_INTERVAL
2280
  )
2281
 
2282
  @app.route('/force_upload', methods=['POST'])
@@ -2289,8 +2280,8 @@ def force_upload():
2289
  else:
2290
  flash("Во время загрузки на Hugging Face произошли ошибки (не все файлы могли быть загружены). Проверьте логи.", 'warning')
2291
  except Exception as e:
2292
- logging.error(f"Error during forced upload: {e}", exc_info=True)
2293
- flash(f"Критическая ошибка при принудительной загрузке на Hugging Face: {e}", 'error')
2294
  return redirect(url_for('admin'))
2295
 
2296
  @app.route('/force_download', methods=['POST'])
@@ -2303,8 +2294,8 @@ def force_download():
2303
  else:
2304
  flash("Не удалось скачать данные с Hugging Face после нескольких попыток. Используются текущие локальные данные. Проверьте логи.", 'error')
2305
  except Exception as e:
2306
- logging.error(f"Error during forced download: {e}", exc_info=True)
2307
- flash(f"Критическая ошибка при принудительном скачивании с Hugging Face: {e}", 'error')
2308
  return redirect(url_for('admin'))
2309
 
2310
 
@@ -2315,20 +2306,13 @@ if __name__ == '__main__':
2315
  load_initial_data()
2316
  logging.info("Initial data load complete.")
2317
 
2318
- if HF_TOKEN_WRITE:
2319
- backup_thread = threading.Thread(target=periodic_backup, daemon=True)
2320
- backup_thread.start()
2321
- logging.info("Periodic backup thread started.")
2322
- else:
2323
- logging.warning("Periodic backup thread *not* started (HF_TOKEN_WRITE not set).")
2324
-
2325
  port = int(os.environ.get('PORT', 7860))
2326
- logging.info(f"Starting Flask app server on host 0.0.0.0 and port {port}")
2327
 
2328
  try:
2329
  from waitress import serve
2330
  serve(app, host='0.0.0.0', port=port, threads=8)
2331
  except ImportError:
2332
- logging.warning("Waitress not found. Falling back to Flask development server.")
2333
  logging.warning("Install waitress for a production-ready server: pip install waitress")
2334
  app.run(debug=False, host='0.0.0.0', port=port)
 
23
  app.config.update(
24
  SESSION_COOKIE_SAMESITE="None",
25
  SESSION_COOKIE_SECURE=True,
26
+ SESSION_COOKIE_HTTPONLY=True,
27
+ PREFERRED_URL_SCHEME="https"
28
  )
29
 
30
  DATA_FILE = 'data_kglobal.json'
 
43
  DOWNLOAD_RETRIES = 3
44
  DOWNLOAD_DELAY = 5
45
  UPLOAD_DELAY = 2
 
46
 
47
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
48
 
 
137
  if 'orders' not in content: content['orders'] = {}
138
  elif file_path == USERS_FILE:
139
  if not isinstance(content, dict): raise ValueError("Users file is not a dictionary")
 
 
 
140
  return content
141
  except (FileNotFoundError, json.JSONDecodeError, ValueError) as e:
142
  logging.error(f"Error loading local file {file_path}: {e}. Returning default.")
 
180
  with open(DATA_FILE, 'w', encoding='utf-8') as file:
181
  json.dump(app_data, file, ensure_ascii=False, indent=4)
182
  logging.info(f"Data successfully saved to {DATA_FILE} and memory cache updated.")
183
+
184
+ upload_db_to_hf(DATA_FILE)
185
+ return True
186
  except Exception as e:
187
  logging.error(f"Error saving data to {DATA_FILE}: {e}", exc_info=True)
188
  return False
 
202
  with open(USERS_FILE, 'w', encoding='utf-8') as file:
203
  json.dump(app_users, file, ensure_ascii=False, indent=4)
204
  logging.info(f"User data successfully saved to {USERS_FILE} and memory cache updated.")
205
+
206
+ upload_db_to_hf(USERS_FILE)
207
+ return True
208
  except Exception as e:
209
  logging.error(f"Error saving user data to {USERS_FILE}: {e}", exc_info=True)
210
  return False
 
222
  for file_name in files_to_upload:
223
  if os.path.exists(file_name):
224
  try:
225
+ api.upload_file(
226
+ path_or_fileobj=file_name,
227
+ path_in_repo=file_name,
228
+ repo_id=REPO_ID,
229
+ repo_type="dataset",
230
+ token=HF_TOKEN_WRITE,
231
+ commit_message=f"Sync {file_name} {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
232
+ )
 
 
233
  logging.info(f"File {file_name} successfully uploaded to Hugging Face.")
234
  time.sleep(UPLOAD_DELAY)
235
  except Exception as e:
 
245
  logging.error(f"General error during Hugging Face upload initialization or process: {e}", exc_info=True)
246
  return False
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  CATALOG_TEMPLATE = '''
249
  <!DOCTYPE html>
250
  <html lang="ru">
 
328
  .cart-item-price { font-size: 0.9rem; color: #444c5a; }
329
  body.dark-mode .cart-item-price { color: #8aa9b5; }
330
  .cart-item-total { font-weight: bold; text-align: right; grid-column: 3; font-size: 1rem; }
331
+ .cart-item-remove { background:none; border:none; color:#f56565; cursor:pointer; font-size: 1.3em; padding: 5px; line-height: 1; }
332
  .cart-item-remove:hover { color: #c53030; }
333
  .quantity-input, .color-select { width: 100%; max-width: 180px; padding: 10px; border: 1px solid #d4d9e3; border-radius: 8px; font-size: 1rem; margin: 10px 0; box-sizing: border-box; }
334
  body.dark-mode .quantity-input, body.dark-mode .color-select { background-color: #1a114f; border-color: #102a50; color: #c8d0e0; }
 
1101
  <button type="submit" class="button download-hf-button" title="Скачать файлы (перезапишет локальные)"><i class="fas fa-download"></i> Скачать БД</button>
1102
  </form>
1103
  </div>
1104
+ <p style="font-size: 0.85rem; color: #5e6e75;">Резервное копирование происходит автоматически после каждого сохранения изменений. Используйте эти кнопки для немедленной синхронизации.</p>
1105
+ <p style="font-size: 0.85rem; color: #5e6e75;">Сохранение данных (товары, пользователи, категории) происходит локально, синхронизация с датацентром - автоматически после сохранения или принудительно.</p>
1106
  </div>
1107
 
1108
 
 
1687
  localStorage.setItem('kglobalUserToken', '{token_attempt}');
1688
  console.log('Stored token in localStorage.');
1689
  }} catch (e) {{ console.error("Error saving token to localStorage:", e); }}
1690
+ if (window.parent && window.parent.location.href !== window.location.href) {{
1691
+ window.parent.location.reload(true); // Attempt to reload parent frame for session re-check
1692
+ }} else {{
1693
+ window.location.href = "{url_for('catalog', _external=True, _scheme=app.config['PREFERRED_URL_SCHEME'])}";
1694
+ }}
1695
  </script>
1696
+ <p>Вход выполнен успешно. Перенаправление в <a href="{url_for('catalog', _external=True, _scheme=app.config['PREFERRED_URL_SCHEME'])}">каталог</a>...</p>
1697
  </body></html>
1698
  '''
1699
  return login_response_html
 
1746
  localStorage.removeItem('kglobalUserToken');
1747
  console.log('Removed stored token from localStorage.');
1748
  }} catch (e) {{ console.error("Error removing from localStorage:", e); }}
1749
+ if (window.parent && window.parent.location.href !== window.location.href) {{
1750
+ window.parent.location.reload(true);
1751
+ }} else {{
1752
+ window.location.href = "{url_for('catalog', _external=True, _scheme=app.config['PREFERRED_URL_SCHEME'])}";
1753
+ }}
1754
  </script>
1755
+ <p>Выход выполнен. Перенаправление на <a href="{url_for('catalog', _external=True, _scheme=app.config['PREFERRED_URL_SCHEME'])}">главную страницу</a>...</p>
1756
  </body></html>
1757
  '''
1758
  return logout_response_html
 
2267
  categories=display_categories,
2268
  users=display_users_sorted_dict,
2269
  repo_id=REPO_ID,
2270
+ currency_code=CURRENCY_CODE
 
2271
  )
2272
 
2273
  @app.route('/force_upload', methods=['POST'])
 
2280
  else:
2281
  flash("Во время загрузки на Hugging Face произошли ошибки (не все файлы могли быть загружены). Проверьте логи.", 'warning')
2282
  except Exception as e:
2283
+ logging.error(f"Critical error during forced upload: {e}", exc_info=True)
2284
+ flash(f"Critical error during forced upload to Hugging Face: {e}", 'error')
2285
  return redirect(url_for('admin'))
2286
 
2287
  @app.route('/force_download', methods=['POST'])
 
2294
  else:
2295
  flash("Не удалось скачать данные с Hugging Face после нескольких попыток. Используются текущие локальные данные. Проверьте логи.", 'error')
2296
  except Exception as e:
2297
+ logging.error(f"Critical error during forced download: {e}", exc_info=True)
2298
+ flash(f"Critical error during forced download from Hugging Face: {e}", 'error')
2299
  return redirect(url_for('admin'))
2300
 
2301
 
 
2306
  load_initial_data()
2307
  logging.info("Initial data load complete.")
2308
 
 
 
 
 
 
 
 
2309
  port = int(os.environ.get('PORT', 7860))
2310
+ logging.info(f"Starting Flask app server on host 0.0.0.0 and port {port}. Ensure HTTPS is configured for session cookies with SameSite=None to work correctly in iframes.")
2311
 
2312
  try:
2313
  from waitress import serve
2314
  serve(app, host='0.0.0.0', port=port, threads=8)
2315
  except ImportError:
2316
+ logging.warning("Waitress not found. Falling back to Flask development server. NOT FOR PRODUCTION USE.")
2317
  logging.warning("Install waitress for a production-ready server: pip install waitress")
2318
  app.run(debug=False, host='0.0.0.0', port=port)