Kgshop commited on
Commit
cc7b121
·
verified ·
1 Parent(s): 514e4a7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +175 -204
app.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import os
2
  from flask import Flask, request, Response, render_template_string, jsonify, redirect, url_for
3
  import hmac
@@ -69,9 +71,10 @@ def download_data_from_hf():
69
  return True
70
  except RepositoryNotFoundError:
71
  logging.error(f"Hugging Face repository '{REPO_ID}' not found. Cannot download data.")
 
72
  except Exception as e:
73
  logging.error(f"Error downloading data from Hugging Face: {e}")
74
- return False
75
 
76
  def load_visitor_data():
77
  global visitor_data_cache
@@ -83,28 +86,25 @@ def load_visitor_data():
83
  logging.info("Visitor data loaded from local JSON.")
84
  except FileNotFoundError:
85
  logging.warning(f"{DATA_FILE} not found locally. Starting with empty data.")
86
- visitor_data_cache = {"organization_details": {}}
87
  except json.JSONDecodeError:
88
  logging.error(f"Error decoding {DATA_FILE}. Starting with empty data.")
89
- visitor_data_cache = {"organization_details": {}}
90
  except Exception as e:
91
  logging.error(f"Unexpected error loading visitor data: {e}")
92
- visitor_data_cache = {"organization_details": {}}
93
 
94
  if "organization_details" not in visitor_data_cache:
95
  visitor_data_cache["organization_details"] = {}
96
 
97
- return visitor_data_cache
98
-
99
  def save_visitor_data():
100
- with _data_lock:
101
- try:
102
- with open(DATA_FILE, 'w', encoding='utf-8') as f:
103
- json.dump(visitor_data_cache, f, ensure_ascii=False, indent=4)
104
- logging.info(f"Visitor data successfully saved to {DATA_FILE}.")
105
- upload_data_to_hf_async()
106
- except Exception as e:
107
- logging.error(f"Error saving visitor data: {e}")
108
 
109
  def upload_data_to_hf():
110
  if not HF_TOKEN_WRITE:
@@ -117,8 +117,7 @@ def upload_data_to_hf():
117
  try:
118
  api = HfApi()
119
  with _data_lock:
120
- file_content_exists = os.path.getsize(DATA_FILE) > 0
121
- if not file_content_exists:
122
  logging.warning(f"{DATA_FILE} is empty. Skipping upload.")
123
  return
124
 
@@ -156,9 +155,7 @@ def verify_telegram_data(init_data_str):
156
  if not received_hash:
157
  return None, False
158
 
159
- data_check_list = []
160
- for key, value in sorted(parsed_data.items()):
161
- data_check_list.append(f"{key}={value[0]}")
162
  data_check_string = "\n".join(data_check_list)
163
 
164
  secret_key = hmac.new("WebAppData".encode(), BOT_TOKEN.encode(), hashlib.sha256).digest()
@@ -166,12 +163,11 @@ def verify_telegram_data(init_data_str):
166
 
167
  if calculated_hash == received_hash:
168
  auth_date = int(parsed_data.get('auth_date', [0])[0])
169
- current_time = int(time.time())
170
- if current_time - auth_date > 86400:
171
- logging.warning(f"Telegram InitData is older than 24 hours (Auth Date: {auth_date}, Current: {current_time}).")
172
  return parsed_data, True
173
  else:
174
- logging.warning(f"Data verification failed. Calculated: {calculated_hash}, Received: {received_hash}")
175
  return parsed_data, False
176
  except Exception as e:
177
  logging.error(f"Error verifying Telegram data: {e}")
@@ -462,7 +458,7 @@ TEMPLATE = """
462
  {% for phone in org_details.phone_numbers %}
463
  <li class="business-card-phone-item">
464
  <a href="tel:{{ phone }}">
465
- <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJtMjIuMDIyIDEuOTY3LTMuMDkgMy41MjNhOC40OCA4LjQ4IDAgMCAwLTEzLjA5OSAwTDEuOTc4IDEuOTY3Yy0uOTc0LTEuMTA3LTIuNTI1LS40MS0yLjUxMiAxLjAzNXYxOC44NjhjLjAwMSAxLjQ0NSAxLjUyOCAyLjEzNiAyLjQ4OCAxLjAzN2wzLjA5LTMuNTIzYTEwLjE2IDEwLjE2IDAgMCAwIDEzLjA5OSAwbDMuMDkgMy41MjNjLjk3NCAxLjEwNyAyLjUyNS40MSAyLjUxMi0xLjAzN1YyLjAwMWMtLjAwMS0xLjQ0NS0xLjUyOC0yLjEzNi0yLjQ4OC0xLjAzN3oiLz48cGF0aCBkPSJtNy41IDIuOTc2YzEuMjI1LjA2MSAyLjQ1LjA5MSAzLjY3NS4wOTFzMi40NS0uMDMxIDMuNjc1LS4wOTFjLjY4Ny0uMDM0IDEuNDMzLjMyNSAxLjcwOS43NDdsMS40NzMgMS42NzhjLjQ5LjU1Ny44MzcgMS4yNjcuODk5IDIuMDgzLjA0MS41NTEuMDYyIDEuMS4wNjIgMS42NnYxLjUwNGMwIC43NzUtLjE3IDEuNTUzLS41MDggMi4yMzMtLjQ3Mi45MjUtMS4xMTcgMS43NDgtMS45MzUgMi4zODhsLS4xMzQuMTA1Yy0uNzU0LjU4My0xLjY0Ny45NzUtMi41NjIgMS4xODQtLjYwMi4xMzYtMS4wMDkuMjA4LTEuNzI1LjIwOC0xLjExNSAwLjc1NC0uMTUzIDMuNjgyLS4xNTMgNS41NDJzLjE1NCA0Ljc4Ni4xNTMgNS41NDJjLjAwMi0uNjU3LS4wNzgtMS4yNy0uMjItMS44MjQtLjE0OC0uNTU4LS40MTctMS4wODgtLjc2NC0xLjU1MS0uNjMyLS44NDUtMS40NDctMS41NTctMi40MTItMi4wNjItLjk2My0uNTA3LTEuOTk0LS44Mi0zLjA0Ni0uOTUyLS44MTMt.MTA0LTEuNTcxLS4xNDUtMi4zMzgtLjA5OS0uNjk0LjAxMS0xLjMzNy4xMDYtMS45MjQuMjg1LS41ODkuMTg0LTEuMTI2LjQyMS0xLjYwMS42OTMtLjQ3Ni4yNzMtLjkwNi41NzQtMS4yOTcuODktLjM4OC4zMTQtLjc0My42NDctMS4wNjcuOTk4LS4zMjYuMzUzLS42NDYuNzIyLS45NTkuMTA1OS0uMzEzLjMyOC0uNjIuNjUzLS45MjEuOTc1LS4yOTQuMzExLS41NzYuNjIzLS44NDQuOTMyLS4yNy4zMDktLjUyMy42MTctLjc1Ny45MTgtLjE5Ny4yNTQtLjM2MS40OTMtLjQ4MS43MjUtLjExOS4yMzItLjE4NS40NTItLjE5My41OTMtLjAwOS4xNDUtLjAxNC4yOTMtLjAxNi40NDF2MS43NmMwIC41NzYtLjE3NSAxLjEyMi0uNDQ3IDEuNTYtLjIyNy4zOTMtLjU2NS41OTktMS4wMTkuNTk5LTEuMTg2LS4wMDEtMS45OTYtMS4zOTctMi4yOTYtMi44NDItLjMyMy0xLjU1OC0uMzIzLTQuNTY5LS4zMjMtNi40MTFzLjAxNS00Ljg1NC4zMjMtNi40MTJjLjI5OS0xLjQ0NSAxLjExLTIuODQxIDIuMjk2LTIuODQyLjQ1NC4wMDcuNzgxLjI1OSAxLjAxOS42MDkuMjE1LjMzNC4zMjMuNzMuMzIzIDEuMTQ3di45NWMuMDMgMS4zMTQtLjAxNSAyLjYxLS4xNDcgMy44NzUtLjEwNiAxLjAzLS4yMzQgMi4wNDYtLjM1MSAyLjk5NmwuNTkzLS4zNzljLjMyNi0uMjA2LjY4Mi0uMzgxIDEuMDQ5LS41NzEuMzg2LS4xOTcgLjc5LS4zNjUgMS4xOTQtLjQ5NC40MDUtLjEyOS43ODctLjIzMyAxLjEyOC0uMjkwLjM0Mi0uMDU4LjYwNC0uMDc0Ljc4Mi0uMDQ3WiIvPjwvc3ZnPg==">
466
  {{ phone }}
467
  </a>
468
  </li>
@@ -1375,31 +1371,23 @@ ADMIN_TEMPLATE = """
1375
  @app.route('/')
1376
  def index():
1377
  user_id_str = request.args.get('user_id_for_test')
1378
- all_data = load_visitor_data()
1379
  user_data = {}
1380
  is_first_visit = False
1381
 
1382
- if user_id_str and user_id_str in all_data:
1383
- user_data = all_data[user_id_str]
1384
  user_data['id'] = user_id_str
1385
-
1386
  is_first_visit = not user_data.get('has_been_welcomed', False)
1387
-
1388
  bonus_history = user_data.get('history', [])
1389
- for item in bonus_history: item['transaction_type'] = 'bonus'
1390
  debt_history = user_data.get('debt_history', [])
 
1391
  for item in debt_history: item['transaction_type'] = 'debt'
1392
-
1393
- combined_history = sorted(bonus_history + debt_history, key=lambda x: x['date'], reverse=True)
1394
- user_data['combined_history'] = combined_history
1395
  user_data['invoices'] = user_data.get('invoices', [])
1396
  else:
1397
- user_data = {
1398
- "id": "N/A", "bonuses": 0, "debts": 0, "history": [], "debt_history": [],
1399
- "combined_history": [], "invoices": [], "referral_code": "N/A"
1400
- }
1401
 
1402
- org_details = all_data.get('organization_details', {})
1403
  return render_template_string(TEMPLATE, user=user_data, org_details=org_details, is_first_visit=is_first_visit)
1404
 
1405
  @app.route('/verify', methods=['POST'])
@@ -1414,8 +1402,7 @@ def verify_data():
1414
  user_info_dict = {}
1415
  if user_data_parsed and 'user' in user_data_parsed:
1416
  try:
1417
- user_json_str = unquote(user_data_parsed['user'][0])
1418
- user_info_dict = json.loads(user_json_str)
1419
  except Exception as e:
1420
  logging.error(f"Could not parse user JSON: {e}")
1421
 
@@ -1423,38 +1410,39 @@ def verify_data():
1423
  tg_user_id = user_info_dict.get('id')
1424
  if tg_user_id:
1425
  now = datetime.now(BISHKEK_TZ)
1426
- all_data = load_visitor_data()
1427
-
1428
- existing_user_key = next((key for key, user in all_data.items() if key != "organization_details" and str(user.get('telegram_id')) == str(tg_user_id)), None)
1429
-
1430
- if existing_user_key:
1431
- user_entry = all_data[existing_user_key]
1432
- user_entry.update({
1433
- 'first_name': user_info_dict.get('first_name'), 'last_name': user_info_dict.get('last_name'),
1434
- 'username': user_info_dict.get('username'), 'photo_url': user_info_dict.get('photo_url'),
1435
- 'visited_at': now.timestamp(), 'visited_at_str': now.strftime('%Y-%m-%d %H:%M:%S')
1436
- })
1437
- user_id_to_save = existing_user_key
1438
- else:
1439
- new_user_id = generate_unique_id(all_data)
1440
- user_entry = {
1441
- 'id': new_user_id, 'telegram_id': tg_user_id,
1442
- 'first_name': user_info_dict.get('first_name'), 'last_name': user_info_dict.get('last_name'),
1443
- 'username': user_info_dict.get('username'), 'photo_url': user_info_dict.get('photo_url'),
1444
- 'is_premium': user_info_dict.get('is_premium', False), 'phone_number': None,
1445
- 'visited_at': now.timestamp(), 'visited_at_str': now.strftime('%Y-%m-%d %H:%M:%S'),
1446
- 'bonuses': 0, 'history': [], 'debts': 0, 'debt_history': [], 'invoices': [],
1447
- 'referral_code': f'PROMO{new_user_id}', 'referred_by': None, 'referrals': [], 'has_been_welcomed': False
1448
- }
1449
- user_id_to_save = new_user_id
1450
-
1451
- all_data[user_id_to_save] = user_entry
1452
- save_visitor_data()
 
 
1453
  return jsonify({"status": "ok", "verified": True, "user_id": user_id_to_save})
1454
  else:
1455
  return jsonify({"status": "error", "verified": True, "message": "User ID not found"}), 400
1456
  else:
1457
- logging.warning(f"Verification failed for user: {user_info_dict.get('id')}")
1458
  return jsonify({"status": "error", "verified": False, "message": "Invalid data"}), 403
1459
  except Exception as e:
1460
  logging.exception("Error in /verify endpoint")
@@ -1466,36 +1454,32 @@ def submit_referral():
1466
  data = request.get_json()
1467
  user_id = str(data.get('user_id'))
1468
  referral_code = data.get('referral_code')
1469
- all_data = load_visitor_data()
1470
-
1471
- if not user_id or user_id not in all_data:
1472
- return jsonify({"status": "error", "message": "Пользователь не найден."}), 404
1473
 
1474
- user = all_data[user_id]
1475
- if user.get('has_been_welcomed', False):
1476
- return jsonify({"status": "ok", "message": "Вы уже прошли этот шаг."}), 200
1477
 
1478
- user['has_been_welcomed'] = True
 
 
1479
 
1480
- if referral_code:
1481
- referrer_id = next((u_id for u_id, u in all_data.items() if u_id != "organization_details" and u.get('referral_code') == referral_code), None)
1482
-
1483
- if not referrer_id:
1484
- return jsonify({"status": "error", "message": "Промокод не найден."}), 404
1485
-
1486
- if referrer_id == user_id:
1487
- return jsonify({"status": "error", "message": "Нельзя использовать свой промокод."}), 400
1488
 
1489
- user['referred_by'] = referrer_id
1490
- referrer = all_data[referrer_id]
1491
- if 'referrals' not in referrer: referrer['referrals'] = []
1492
- referrer['referrals'].append(user_id)
 
 
 
 
 
 
 
1493
 
1494
  save_visitor_data()
1495
- return jsonify({"status": "ok", "message": "Промокод успешно применен!"}), 200
1496
- else:
1497
- save_visitor_data()
1498
- return jsonify({"status": "ok", "message": "Добро пожаловать!"}), 200
1499
 
1500
  except Exception as e:
1501
  logging.exception("Error in /submit_referral endpoint")
@@ -1503,20 +1487,17 @@ def submit_referral():
1503
 
1504
  @app.route('/admin')
1505
  def admin_panel():
1506
- all_data = load_visitor_data()
1507
  users_list = []
1508
-
1509
- user_name_map = {uid: f"{ud.get('first_name', '')} {ud.get('last_name', '')}".strip() or f"ID: {uid}" for uid, ud in all_data.items() if uid != "organization_details"}
1510
 
1511
- for user_id, user_data in all_data.items():
1512
  if user_id == "organization_details": continue
1513
- user_data['id'] = user_id
1514
-
1515
- user_data['referrals_count'] = len(user_data.get('referrals', []))
1516
- referrer_id = user_data.get('referred_by')
1517
- user_data['referrer_info'] = user_name_map.get(referrer_id, None)
1518
-
1519
- users_list.append(user_data)
1520
 
1521
  total_users = len(users_list)
1522
  total_bonuses = sum(u.get('bonuses', 0) for u in users_list)
@@ -1535,25 +1516,24 @@ def add_client():
1535
  data = request.get_json()
1536
  phone_number = data.get('phone_number')
1537
  first_name = data.get('first_name')
1538
-
1539
  if not phone_number or not first_name:
1540
  return jsonify({"status": "error", "message": "Имя и номер телефона обязательны."}), 400
1541
 
1542
- all_data = load_visitor_data()
1543
- if any(u.get('phone_number') == phone_number for k, u in all_data.items() if k != "organization_details"):
1544
- return jsonify({"status": "error", "message": "Клиент с таким номером телефона уже существует."}), 409
1545
-
1546
- now = datetime.now(BISHKEK_TZ)
1547
- new_id = generate_unique_id(all_data)
1548
- new_client = {
1549
- 'id': new_id, 'telegram_id': None, 'first_name': first_name, 'last_name': None,
1550
- 'username': None, 'photo_url': None, 'is_premium': False, 'phone_number': phone_number,
1551
- 'visited_at': now.timestamp(), 'visited_at_str': now.strftime('%Y-%m-%d %H:%M:%S'),
1552
- 'bonuses': 0, 'history': [], 'debts': 0, 'debt_history': [], 'invoices': [],
1553
- 'referral_code': f'PROMO{new_id}', 'referred_by': None, 'referrals': [], 'has_been_welcomed': True
1554
- }
1555
- all_data[new_id] = new_client
1556
- save_visitor_data()
1557
  return jsonify({"status": "ok", "message": "Client added successfully"}), 201
1558
  except Exception as e:
1559
  logging.exception("Error in /admin/add_client endpoint")
@@ -1568,31 +1548,29 @@ def add_transaction():
1568
  deduct_amount = float(data.get('deduct_amount', 0))
1569
  add_debt_amount = float(data.get('add_debt_amount', 0))
1570
  repay_debt_amount = float(data.get('repay_debt_amount', 0))
1571
-
1572
  if not user_id: return jsonify({"status": "error", "message": "User ID is required"}), 400
1573
- all_data = load_visitor_data()
1574
- if user_id not in all_data: return jsonify({"status": "error", "message": "User not found"}), 404
1575
-
1576
- user = all_data[user_id]
1577
- now = datetime.now(BISHKEK_TZ)
1578
- now_iso, now_str = now.isoformat(), now.strftime('%Y-%m-%d %H:%M:%S')
1579
 
1580
- if deduct_amount > user.get('bonuses', 0): return jsonify({"status": "error", "message": "Недостаточно бонусов для списания"}), 400
1581
- if repay_debt_amount > user.get('debts', 0): return jsonify({"status": "error", "message": "Сумма погашения превышает текущий долг"}), 400
1582
-
1583
- accrual_amount = purchase_amount * 0.02
1584
- user['bonuses'] = round(user.get('bonuses', 0) + accrual_amount - deduct_amount, 2)
1585
- if 'history' not in user: user['history'] = []
1586
- if accrual_amount > 0: user['history'].append({"type": "accrual", "amount": round(accrual_amount, 2), "description": f"Начисление с покупки {round(purchase_amount, 2)}", "date": now_iso, "date_str": now_str})
1587
- if deduct_amount > 0: user['history'].append({"type": "deduction", "amount": round(deduct_amount, 2), "description": "Списание бонусов", "date": now_iso, "date_str": now_str})
1588
-
1589
- user['debts'] = round(user.get('debts', 0) + add_debt_amount - repay_debt_amount, 2)
1590
- if 'debt_history' not in user: user['debt_history'] = []
1591
- if add_debt_amount > 0: user['debt_history'].append({"type": "accrual", "amount": round(add_debt_amount, 2), "description": "Добавление долга", "date": now_iso, "date_str": now_str})
1592
- if repay_debt_amount > 0: user['debt_history'].append({"type": "payment", "amount": round(repay_debt_amount, 2), "description": "Погашение долга", "date": now_iso, "date_str": now_str})
1593
-
1594
- save_visitor_data()
1595
- return jsonify({"status": "ok", "message": "Transaction successful", "new_balance": user['bonuses'], "new_debt": user['debts']}), 200
 
 
 
 
 
1596
  except Exception as e:
1597
  logging.exception("Error in /admin/add_transaction endpoint")
1598
  return jsonify({"status": "error", "message": str(e)}), 500
@@ -1604,25 +1582,20 @@ def add_invoice():
1604
  user_id = str(data.get('user_id'))
1605
  total_amount = float(data.get('total_amount', 0))
1606
  items = data.get('items', [])
1607
-
1608
  if not user_id: return jsonify({"status": "error", "message": "User ID is required"}), 400
1609
  if not items: return jsonify({"status": "error", "message": "Необходимо добавить товары в накладную."}), 400
1610
 
1611
- all_data = load_visitor_data()
1612
- if user_id not in all_data: return jsonify({"status": "error", "message": "User not found"}), 404
1613
-
1614
- user = all_data[user_id]
1615
- now = datetime.now(BISHKEK_TZ)
1616
- now_iso, now_str = now.isoformat(), now.strftime('%Y-%m-%d %H:%M:%S')
1617
- invoice_id = str(uuid.uuid4().hex[:8]).upper()
1618
-
1619
- processed_items = [{"product_name": item.get('product_name'), "quantity": float(item.get('quantity', 0)), "unit_price": float(item.get('unit_price', 0)), "item_total": round(float(item.get('quantity', 0)) * float(item.get('unit_price', 0)), 2)} for item in items]
1620
- new_invoice = {"invoice_id": invoice_id, "date": now_iso, "date_str": now_str, "total_amount": round(total_amount, 2), "items": processed_items}
1621
-
1622
- if 'invoices' not in user: user['invoices'] = []
1623
- user['invoices'].append(new_invoice)
1624
-
1625
- save_visitor_data()
1626
  return jsonify({"status": "ok", "message": "Invoice added successfully", "invoice_id": invoice_id}), 200
1627
  except Exception as e:
1628
  logging.exception("Error in /admin/add_invoice endpoint")
@@ -1635,17 +1608,14 @@ def delete_invoice():
1635
  user_id, invoice_id = str(data.get('user_id')), data.get('invoice_id')
1636
  if not user_id or not invoice_id: return jsonify({"status": "error", "message": "User ID and Invoice ID are required"}), 400
1637
 
1638
- all_data = load_visitor_data()
1639
- if user_id not in all_data: return jsonify({"status": "error", "message": "User not found"}), 404
1640
-
1641
- user = all_data[user_id]
1642
- if 'invoices' not in user: return jsonify({"status": "error", "message": "User has no invoices"}), 404
1643
-
1644
- original_count = len(user['invoices'])
1645
- user['invoices'] = [inv for inv in user['invoices'] if inv.get('invoice_id') != invoice_id]
1646
- if len(user['invoices']) == original_count: return jsonify({"status": "error", "message": "Invoice not found for this user"}), 404
1647
-
1648
- save_visitor_data()
1649
  return jsonify({"status": "ok", "message": "Invoice deleted successfully"}), 200
1650
  except Exception as e:
1651
  logging.exception("Error in /admin/delete_invoice endpoint")
@@ -1655,21 +1625,21 @@ def delete_invoice():
1655
  def delete_client():
1656
  try:
1657
  user_id = str(request.get_json().get('user_id'))
1658
- if not user_id:
1659
- return jsonify({"status": "error", "message": "User ID is required"}), 400
1660
-
1661
- all_data = load_visitor_data()
1662
-
1663
- if user_id not in all_data:
1664
- return jsonify({"status": "error", "message": "User not found"}), 404
1665
-
1666
- if all_data[user_id].get('telegram_id') is not None:
1667
- return jsonify({"status": "error", "message": "Cannot delete a Telegram-linked user"}), 403
1668
-
1669
- del all_data[user_id]
1670
-
1671
- save_visitor_data()
1672
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1673
  return jsonify({"status": "ok", "message": "Client deleted successfully"}), 200
1674
  except Exception as e:
1675
  logging.exception("Error in /admin/delete_client endpoint")
@@ -1678,8 +1648,7 @@ def delete_client():
1678
  @app.route('/admin/organization_details', methods=['GET'])
1679
  def get_organization_details():
1680
  try:
1681
- all_data = load_visitor_data()
1682
- return jsonify(all_data.get('organization_details', {})), 200
1683
  except Exception as e:
1684
  logging.exception("Error getting organization details")
1685
  return jsonify({"status": "error", "message": str(e)}), 500
@@ -1688,14 +1657,13 @@ def get_organization_details():
1688
  def save_organization_details():
1689
  try:
1690
  data = request.get_json()
1691
- new_org_details = {
1692
- "name": data.get("name", ""), "phone_numbers": data.get("phone_numbers", []),
1693
- "address": data.get("address", ""), "whatsapp_link": data.get("whatsapp_link", ""),
1694
- "telegram_link": data.get("telegram_link", "")
1695
- }
1696
- all_data = load_visitor_data()
1697
- all_data['organization_details'] = new_org_details
1698
- save_visitor_data()
1699
  return jsonify({"status": "ok", "message": "Organization details saved successfully"}), 200
1700
  except Exception as e:
1701
  logging.exception("Error saving organization details")
@@ -1705,26 +1673,29 @@ if __name__ == '__main__':
1705
  print("--- BONUS SYSTEM SERVER ---")
1706
  print(f"Server starting on http://{HOST}:{PORT}")
1707
 
1708
- print("Loading local data file...")
1709
  load_visitor_data()
1710
 
1711
- if (not visitor_data_cache or len(visitor_data_cache) <= 1) and HF_TOKEN_READ:
1712
- print("Local data not found or empty. Attempting to restore from Hugging Face backup...")
1713
- download_data_from_hf()
1714
- elif not visitor_data_cache or len(visitor_data_cache) <= 1:
1715
- print("Starting with a fresh, empty database.")
 
 
 
1716
  else:
1717
- print("Successfully loaded data from local file.")
 
1718
 
1719
- if not HF_TOKEN_READ or not HF_TOKEN_WRITE:
1720
- print("WARNING: Hugging Face token(s) not set. Backup/restore functionality will be limited.")
1721
-
1722
  print("WARNING: The /admin route is NOT protected. Implement proper authentication for production.")
1723
-
1724
  if HF_TOKEN_WRITE:
1725
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
1726
  backup_thread.start()
1727
  print("Periodic backup thread started (every hour).")
 
 
1728
 
1729
  print("--- Server Ready ---")
1730
  app.run(host=HOST, port=PORT, debug=False)
 
1
+
2
+
3
  import os
4
  from flask import Flask, request, Response, render_template_string, jsonify, redirect, url_for
5
  import hmac
 
71
  return True
72
  except RepositoryNotFoundError:
73
  logging.error(f"Hugging Face repository '{REPO_ID}' not found. Cannot download data.")
74
+ return False
75
  except Exception as e:
76
  logging.error(f"Error downloading data from Hugging Face: {e}")
77
+ return False
78
 
79
  def load_visitor_data():
80
  global visitor_data_cache
 
86
  logging.info("Visitor data loaded from local JSON.")
87
  except FileNotFoundError:
88
  logging.warning(f"{DATA_FILE} not found locally. Starting with empty data.")
89
+ visitor_data_cache = {}
90
  except json.JSONDecodeError:
91
  logging.error(f"Error decoding {DATA_FILE}. Starting with empty data.")
92
+ visitor_data_cache = {}
93
  except Exception as e:
94
  logging.error(f"Unexpected error loading visitor data: {e}")
95
+ visitor_data_cache = {}
96
 
97
  if "organization_details" not in visitor_data_cache:
98
  visitor_data_cache["organization_details"] = {}
99
 
 
 
100
  def save_visitor_data():
101
+ try:
102
+ with open(DATA_FILE, 'w', encoding='utf-8') as f:
103
+ json.dump(visitor_data_cache, f, ensure_ascii=False, indent=4)
104
+ logging.info(f"Visitor data successfully saved to {DATA_FILE}.")
105
+ upload_data_to_hf_async()
106
+ except Exception as e:
107
+ logging.error(f"Error saving visitor data: {e}")
 
108
 
109
  def upload_data_to_hf():
110
  if not HF_TOKEN_WRITE:
 
117
  try:
118
  api = HfApi()
119
  with _data_lock:
120
+ if not os.path.getsize(DATA_FILE) > 0:
 
121
  logging.warning(f"{DATA_FILE} is empty. Skipping upload.")
122
  return
123
 
 
155
  if not received_hash:
156
  return None, False
157
 
158
+ data_check_list = [f"{key}={value[0]}" for key, value in sorted(parsed_data.items())]
 
 
159
  data_check_string = "\n".join(data_check_list)
160
 
161
  secret_key = hmac.new("WebAppData".encode(), BOT_TOKEN.encode(), hashlib.sha256).digest()
 
163
 
164
  if calculated_hash == received_hash:
165
  auth_date = int(parsed_data.get('auth_date', [0])[0])
166
+ if (int(time.time()) - auth_date) > 86400:
167
+ logging.warning(f"Telegram InitData is older than 24 hours.")
 
168
  return parsed_data, True
169
  else:
170
+ logging.warning(f"Data verification failed.")
171
  return parsed_data, False
172
  except Exception as e:
173
  logging.error(f"Error verifying Telegram data: {e}")
 
458
  {% for phone in org_details.phone_numbers %}
459
  <li class="business-card-phone-item">
460
  <a href="tel:{{ phone }}">
461
+ <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q2vb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJtMjIuMDIyIDEuOTY3LTMuMDkgMy41MjNhOC40OCA4LjQ4IDAgMCAwLTEzLjA5OSAwTDEuOTc4IDEuOTY3Yy0uOTc0LTEuMTA3LTIuNTI1LS40MS0yLjUxMiAxLjAzNXYxOC44NjhjLjAwMSAxLjQ0NSAxLjUyOCAyLjEzNiAyLjQ4OCAxLjAzN2wzLjA5LTMuNTIzYTEwLjE2IDEwLjE2IDAgMCAwIDEzLjA5OSAwbDMuMDkgMy41MjNjLjk3NCAxLjEwNyAyLjUyNS40MSAyLjUxMi0xLjAzN1YyLjAwMWMtLjAwMS0xLjQ0NS0xLjUyOC0yLjEzNi0yLjQ4OC0xLjAzN3oiLz48cGF0aCBkPSJtNy41IDIuOTc2YzEuMjI1LjA2MSAyLjQ1LjA5MSAzLjY3NS4wOTFzMi40NS0uMDMxIDMuNjc1LS4wOTFjLjY4Ny0uMDM0IDEuNDMzLjMyNSAxLjcwOS43NDdsMS40NzMgMS42NzhjLjQ5LjU1Ny44MzcgMS4yNjcuODk5IDIuMDgzLjA0MS41NTEuMDYyIDEuMS4wNjIgMS42NnYxLjUwNGMwIC43NzUtLjE3IDEuNTUzLS41MDggMi4yMzMtLjQ3Mi45MjUtMS4xMTcgMS43NDgtMS45MzUgMi4zODhsLS4xMzQuMTA1Yy0uNzU0LjU4My0xLjY0Ny45NzUtMi41NjIgMS4xODQtLjYwMi4xMzYtMS4wMDkuMjA4LTEuNzI1LjIwOC0xLjExNSAwLjc1NC0uMTUzIDMuNjgyLS4xNTMgNS41NDJzLjE1NCA0Ljc4Ni4xNTMgNS41NDJjLjAwMi0uNjU3LS4wNzgtMS4yNy0uMjItMS44MjQtLjE0OC0uNTU4LS40MTctMS4wODgtLjc2NC0xLjU1MS0uNjMyLS44NDUtMS40NDctMS41NTctMi40MTItMi4wNjItLjk2My0uNTA3LTEuOTk0LS44Mi0zLjA0Ni0uOTUyLS44MTMt.MTA0LTEuNTcxLS4xNDUtMi4zMzgtLjA5OS0uNjk0LjAxMS0xLjMzNy4xMDYtMS45MjQuMjg1LS41ODkuMTg0LTEuMTI2LjQyMS0xLjYwMS42OTMtLjQ3Ni4yNzMtLjkwNi41NzQtMS4yOTcuODktLjM4OC4zMTQtLjc0My42NDctMS4wNjcuOTk4LS4zMjYuMzUzLS42NDYuNzIyLS45NTkuMTA1OS0uMzEzLjMyOC0uNjIuNjUzLS45MjEuOTc1LS4yOTQuMzExLS41NzYuNjIzLS44NDQuOTMyLS4yNy4zMDktLjUyMy42MTctLjc1Ny45MTgtLjE5Ny4yNTQtLjM2MS40OTMtLjQ4MS43MjUtLjExOS4yMzItLjE4NS40NTItLjE5My41OTMtLjAwOS4xNDUtLjAxNC4yOTMtLjAxNi40NDF2MS43NmMwIC41NzYtLjE3NSAxLjEyMi0uNDQ3IDEuNTYtLjIyNy4zOTMtLjU2NS41OTktMS4wMTkuNTk5LTEuMTg2LS4wMDEtMS45OTYtMS4zOTctMi4yOTYtMi44NDItLjMyMy0xLjU1OC0uMzIzLTQuNTY5LS4zMjMtNi40MTFzLjAxNS00Ljg1NC4zMjMtNi40MTJjLjI5OS0xLjQ0NSAxLjExLTIuODQxIDIuMjk2LTIuODQyLjQ1NC4wMDcuNzgxLjI1OSAxLjAxOS42MDkuMjE1LjMzNC4zMjMuNzMuMzIzIDEuMTQ3di45NWMuMDMgMS4zMTQtLjAxNSAyLjYxLS4xNDcgMy44NzUtLjEwNiAxLjAzLS4yMzQgMi4wNDYtLjM1MSAyLjk5NmwuNTkzLS4zNzljLjMyNi0uMjA2LjY4Mi0uMzgxIDEuMDQ5LS41NzEuMzg2LS4xOTcgLjc5LS4zNjUgMS4xOTQtLjQ5NC40MDUtLjEyOS43ODctLjIzMyAxLjEyOC0uMjkwLjM0Mi0uMDU4LjYwNC0uMDc0Ljc4Mi0uMDQ3WiIvPjwvc3ZnPg==">
462
  {{ phone }}
463
  </a>
464
  </li>
 
1371
  @app.route('/')
1372
  def index():
1373
  user_id_str = request.args.get('user_id_for_test')
 
1374
  user_data = {}
1375
  is_first_visit = False
1376
 
1377
+ if user_id_str and user_id_str in visitor_data_cache:
1378
+ user_data = visitor_data_cache[user_id_str]
1379
  user_data['id'] = user_id_str
 
1380
  is_first_visit = not user_data.get('has_been_welcomed', False)
 
1381
  bonus_history = user_data.get('history', [])
 
1382
  debt_history = user_data.get('debt_history', [])
1383
+ for item in bonus_history: item['transaction_type'] = 'bonus'
1384
  for item in debt_history: item['transaction_type'] = 'debt'
1385
+ user_data['combined_history'] = sorted(bonus_history + debt_history, key=lambda x: x['date'], reverse=True)
 
 
1386
  user_data['invoices'] = user_data.get('invoices', [])
1387
  else:
1388
+ user_data = {"id": "N/A", "bonuses": 0, "debts": 0, "combined_history": [], "invoices": [], "referral_code": "N/A"}
 
 
 
1389
 
1390
+ org_details = visitor_data_cache.get('organization_details', {})
1391
  return render_template_string(TEMPLATE, user=user_data, org_details=org_details, is_first_visit=is_first_visit)
1392
 
1393
  @app.route('/verify', methods=['POST'])
 
1402
  user_info_dict = {}
1403
  if user_data_parsed and 'user' in user_data_parsed:
1404
  try:
1405
+ user_info_dict = json.loads(unquote(user_data_parsed['user'][0]))
 
1406
  except Exception as e:
1407
  logging.error(f"Could not parse user JSON: {e}")
1408
 
 
1410
  tg_user_id = user_info_dict.get('id')
1411
  if tg_user_id:
1412
  now = datetime.now(BISHKEK_TZ)
1413
+ user_id_to_save = None
1414
+
1415
+ with _data_lock:
1416
+ existing_user_key = next((k for k, u in visitor_data_cache.items() if k != "organization_details" and str(u.get('telegram_id')) == str(tg_user_id)), None)
1417
+
1418
+ if existing_user_key:
1419
+ user_entry = visitor_data_cache[existing_user_key]
1420
+ user_entry.update({
1421
+ 'first_name': user_info_dict.get('first_name'), 'last_name': user_info_dict.get('last_name'),
1422
+ 'username': user_info_dict.get('username'), 'photo_url': user_info_dict.get('photo_url'),
1423
+ 'visited_at': now.timestamp(), 'visited_at_str': now.strftime('%Y-%m-%d %H:%M:%S')
1424
+ })
1425
+ user_id_to_save = existing_user_key
1426
+ else:
1427
+ new_user_id = generate_unique_id(visitor_data_cache)
1428
+ user_entry = {
1429
+ 'id': new_user_id, 'telegram_id': tg_user_id,
1430
+ 'first_name': user_info_dict.get('first_name'), 'last_name': user_info_dict.get('last_name'),
1431
+ 'username': user_info_dict.get('username'), 'photo_url': user_info_dict.get('photo_url'),
1432
+ 'is_premium': user_info_dict.get('is_premium', False), 'phone_number': None,
1433
+ 'visited_at': now.timestamp(), 'visited_at_str': now.strftime('%Y-%m-%d %H:%M:%S'),
1434
+ 'bonuses': 0, 'history': [], 'debts': 0, 'debt_history': [], 'invoices': [],
1435
+ 'referral_code': f'PROMO{new_user_id}', 'referred_by': None, 'referrals': [], 'has_been_welcomed': False
1436
+ }
1437
+ visitor_data_cache[new_user_id] = user_entry
1438
+ user_id_to_save = new_user_id
1439
+
1440
+ save_visitor_data()
1441
+
1442
  return jsonify({"status": "ok", "verified": True, "user_id": user_id_to_save})
1443
  else:
1444
  return jsonify({"status": "error", "verified": True, "message": "User ID not found"}), 400
1445
  else:
 
1446
  return jsonify({"status": "error", "verified": False, "message": "Invalid data"}), 403
1447
  except Exception as e:
1448
  logging.exception("Error in /verify endpoint")
 
1454
  data = request.get_json()
1455
  user_id = str(data.get('user_id'))
1456
  referral_code = data.get('referral_code')
 
 
 
 
1457
 
1458
+ with _data_lock:
1459
+ if not user_id or user_id not in visitor_data_cache:
1460
+ return jsonify({"status": "error", "message": "Пользователь не найден."}), 404
1461
 
1462
+ user = visitor_data_cache[user_id]
1463
+ if user.get('has_been_welcomed', False):
1464
+ return jsonify({"status": "ok", "message": "Вы уже прошли этот шаг."}), 200
1465
 
1466
+ user['has_been_welcomed'] = True
 
 
 
 
 
 
 
1467
 
1468
+ if referral_code:
1469
+ referrer = next((u for u_id, u in visitor_data_cache.items() if u_id != "organization_details" and u.get('referral_code') == referral_code), None)
1470
+
1471
+ if not referrer:
1472
+ return jsonify({"status": "error", "message": "Промокод не найден."}), 404
1473
+ if referrer['id'] == user_id:
1474
+ return jsonify({"status": "error", "message": "Нельзя использовать свой промокод."}), 400
1475
+
1476
+ user['referred_by'] = referrer['id']
1477
+ if 'referrals' not in referrer: referrer['referrals'] = []
1478
+ referrer['referrals'].append(user_id)
1479
 
1480
  save_visitor_data()
1481
+ message = "Промокод успешно применен!" if referral_code else "Добро пожаловать!"
1482
+ return jsonify({"status": "ok", "message": message}), 200
 
 
1483
 
1484
  except Exception as e:
1485
  logging.exception("Error in /submit_referral endpoint")
 
1487
 
1488
  @app.route('/admin')
1489
  def admin_panel():
 
1490
  users_list = []
1491
+ user_name_map = {uid: f"{ud.get('first_name', '')} {ud.get('last_name', '')}".strip() or f"ID: {uid}" for uid, ud in visitor_data_cache.items() if uid != "organization_details"}
 
1492
 
1493
+ for user_id, user_data in visitor_data_cache.items():
1494
  if user_id == "organization_details": continue
1495
+ user_data_copy = user_data.copy()
1496
+ user_data_copy['id'] = user_id
1497
+ user_data_copy['referrals_count'] = len(user_data_copy.get('referrals', []))
1498
+ referrer_id = user_data_copy.get('referred_by')
1499
+ user_data_copy['referrer_info'] = user_name_map.get(referrer_id, None)
1500
+ users_list.append(user_data_copy)
 
1501
 
1502
  total_users = len(users_list)
1503
  total_bonuses = sum(u.get('bonuses', 0) for u in users_list)
 
1516
  data = request.get_json()
1517
  phone_number = data.get('phone_number')
1518
  first_name = data.get('first_name')
 
1519
  if not phone_number or not first_name:
1520
  return jsonify({"status": "error", "message": "Имя и номер телефона обязательны."}), 400
1521
 
1522
+ with _data_lock:
1523
+ if any(u.get('phone_number') == phone_number for k, u in visitor_data_cache.items() if k != "organization_details"):
1524
+ return jsonify({"status": "error", "message": "Клиент с таким номером телефона уже существует."}), 409
1525
+
1526
+ now = datetime.now(BISHKEK_TZ)
1527
+ new_id = generate_unique_id(visitor_data_cache)
1528
+ new_client = {
1529
+ 'id': new_id, 'telegram_id': None, 'first_name': first_name, 'last_name': None,
1530
+ 'username': None, 'photo_url': None, 'is_premium': False, 'phone_number': phone_number,
1531
+ 'visited_at': now.timestamp(), 'visited_at_str': now.strftime('%Y-%m-%d %H:%M:%S'),
1532
+ 'bonuses': 0, 'history': [], 'debts': 0, 'debt_history': [], 'invoices': [],
1533
+ 'referral_code': f'PROMO{new_id}', 'referred_by': None, 'referrals': [], 'has_been_welcomed': True
1534
+ }
1535
+ visitor_data_cache[new_id] = new_client
1536
+ save_visitor_data()
1537
  return jsonify({"status": "ok", "message": "Client added successfully"}), 201
1538
  except Exception as e:
1539
  logging.exception("Error in /admin/add_client endpoint")
 
1548
  deduct_amount = float(data.get('deduct_amount', 0))
1549
  add_debt_amount = float(data.get('add_debt_amount', 0))
1550
  repay_debt_amount = float(data.get('repay_debt_amount', 0))
 
1551
  if not user_id: return jsonify({"status": "error", "message": "User ID is required"}), 400
 
 
 
 
 
 
1552
 
1553
+ with _data_lock:
1554
+ if user_id not in visitor_data_cache: return jsonify({"status": "error", "message": "User not found"}), 404
1555
+ user = visitor_data_cache[user_id]
1556
+ now = datetime.now(BISHKEK_TZ)
1557
+ now_iso, now_str = now.isoformat(), now.strftime('%Y-%m-%d %H:%M:%S')
1558
+ if deduct_amount > user.get('bonuses', 0): return jsonify({"status": "error", "message": "Недостаточно бонусов для списания"}), 400
1559
+ if repay_debt_amount > user.get('debts', 0): return jsonify({"status": "error", "message": "Сумма погашения превышает текущий долг"}), 400
1560
+
1561
+ accrual_amount = purchase_amount * 0.02
1562
+ user['bonuses'] = round(user.get('bonuses', 0) + accrual_amount - deduct_amount, 2)
1563
+ if 'history' not in user: user['history'] = []
1564
+ if accrual_amount > 0: user['history'].append({"type": "accrual", "amount": round(accrual_amount, 2), "description": f"Начисление с покупки {round(purchase_amount, 2)}", "date": now_iso, "date_str": now_str})
1565
+ if deduct_amount > 0: user['history'].append({"type": "deduction", "amount": round(deduct_amount, 2), "description": "Списание бонусов", "date": now_iso, "date_str": now_str})
1566
+
1567
+ user['debts'] = round(user.get('debts', 0) + add_debt_amount - repay_debt_amount, 2)
1568
+ if 'debt_history' not in user: user['debt_history'] = []
1569
+ if add_debt_amount > 0: user['debt_history'].append({"type": "accrual", "amount": round(add_debt_amount, 2), "description": "Добавление долга", "date": now_iso, "date_str": now_str})
1570
+ if repay_debt_amount > 0: user['debt_history'].append({"type": "payment", "amount": round(repay_debt_amount, 2), "description": "Погашение долга", "date": now_iso, "date_str": now_str})
1571
+
1572
+ save_visitor_data()
1573
+ return jsonify({"status": "ok", "message": "Transaction successful", "new_balance": user['bonuses'], "new_debt": user['debts']}), 200
1574
  except Exception as e:
1575
  logging.exception("Error in /admin/add_transaction endpoint")
1576
  return jsonify({"status": "error", "message": str(e)}), 500
 
1582
  user_id = str(data.get('user_id'))
1583
  total_amount = float(data.get('total_amount', 0))
1584
  items = data.get('items', [])
 
1585
  if not user_id: return jsonify({"status": "error", "message": "User ID is required"}), 400
1586
  if not items: return jsonify({"status": "error", "message": "Необходимо добавить товары в накладную."}), 400
1587
 
1588
+ with _data_lock:
1589
+ if user_id not in visitor_data_cache: return jsonify({"status": "error", "message": "User not found"}), 404
1590
+ user = visitor_data_cache[user_id]
1591
+ now = datetime.now(BISHKEK_TZ)
1592
+ now_iso, now_str = now.isoformat(), now.strftime('%Y-%m-%d %H:%M:%S')
1593
+ invoice_id = str(uuid.uuid4().hex[:8]).upper()
1594
+ processed_items = [{"product_name": item.get('product_name'), "quantity": float(item.get('quantity', 0)), "unit_price": float(item.get('unit_price', 0)), "item_total": round(float(item.get('quantity', 0)) * float(item.get('unit_price', 0)), 2)} for item in items]
1595
+ new_invoice = {"invoice_id": invoice_id, "date": now_iso, "date_str": now_str, "total_amount": round(total_amount, 2), "items": processed_items}
1596
+ if 'invoices' not in user: user['invoices'] = []
1597
+ user['invoices'].append(new_invoice)
1598
+ save_visitor_data()
 
 
 
 
1599
  return jsonify({"status": "ok", "message": "Invoice added successfully", "invoice_id": invoice_id}), 200
1600
  except Exception as e:
1601
  logging.exception("Error in /admin/add_invoice endpoint")
 
1608
  user_id, invoice_id = str(data.get('user_id')), data.get('invoice_id')
1609
  if not user_id or not invoice_id: return jsonify({"status": "error", "message": "User ID and Invoice ID are required"}), 400
1610
 
1611
+ with _data_lock:
1612
+ if user_id not in visitor_data_cache: return jsonify({"status": "error", "message": "User not found"}), 404
1613
+ user = visitor_data_cache[user_id]
1614
+ if 'invoices' not in user: return jsonify({"status": "error", "message": "User has no invoices"}), 404
1615
+ original_count = len(user['invoices'])
1616
+ user['invoices'] = [inv for inv in user['invoices'] if inv.get('invoice_id') != invoice_id]
1617
+ if len(user['invoices']) == original_count: return jsonify({"status": "error", "message": "Invoice not found for this user"}), 404
1618
+ save_visitor_data()
 
 
 
1619
  return jsonify({"status": "ok", "message": "Invoice deleted successfully"}), 200
1620
  except Exception as e:
1621
  logging.exception("Error in /admin/delete_invoice endpoint")
 
1625
  def delete_client():
1626
  try:
1627
  user_id = str(request.get_json().get('user_id'))
1628
+ if not user_id: return jsonify({"status": "error", "message": "User ID is required"}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
1629
 
1630
+ with _data_lock:
1631
+ if user_id not in visitor_data_cache: return jsonify({"status": "error", "message": "User not found"}), 404
1632
+ if visitor_data_cache[user_id].get('telegram_id') is not None: return jsonify({"status": "error", "message": "Cannot delete a Telegram-linked user"}), 403
1633
+
1634
+ user_to_delete = visitor_data_cache[user_id]
1635
+ referrer_id = user_to_delete.get('referred_by')
1636
+ if referrer_id and referrer_id in visitor_data_cache:
1637
+ referrer = visitor_data_cache[referrer_id]
1638
+ if 'referrals' in referrer and user_id in referrer['referrals']:
1639
+ referrer['referrals'].remove(user_id)
1640
+
1641
+ del visitor_data_cache[user_id]
1642
+ save_visitor_data()
1643
  return jsonify({"status": "ok", "message": "Client deleted successfully"}), 200
1644
  except Exception as e:
1645
  logging.exception("Error in /admin/delete_client endpoint")
 
1648
  @app.route('/admin/organization_details', methods=['GET'])
1649
  def get_organization_details():
1650
  try:
1651
+ return jsonify(visitor_data_cache.get('organization_details', {})), 200
 
1652
  except Exception as e:
1653
  logging.exception("Error getting organization details")
1654
  return jsonify({"status": "error", "message": str(e)}), 500
 
1657
  def save_organization_details():
1658
  try:
1659
  data = request.get_json()
1660
+ with _data_lock:
1661
+ visitor_data_cache['organization_details'] = {
1662
+ "name": data.get("name", ""), "phone_numbers": data.get("phone_numbers", []),
1663
+ "address": data.get("address", ""), "whatsapp_link": data.get("whatsapp_link", ""),
1664
+ "telegram_link": data.get("telegram_link", "")
1665
+ }
1666
+ save_visitor_data()
 
1667
  return jsonify({"status": "ok", "message": "Organization details saved successfully"}), 200
1668
  except Exception as e:
1669
  logging.exception("Error saving organization details")
 
1673
  print("--- BONUS SYSTEM SERVER ---")
1674
  print(f"Server starting on http://{HOST}:{PORT}")
1675
 
1676
+ print("Attempting to load local data file...")
1677
  load_visitor_data()
1678
 
1679
+ if not visitor_data_cache or len(visitor_data_cache) <= 1:
1680
+ print("Local data file not found or is empty.")
1681
+ if HF_TOKEN_READ:
1682
+ print("Attempting to restore data from Hugging Face...")
1683
+ if not download_data_from_hf():
1684
+ print("Failed to restore from Hugging Face. Starting fresh.")
1685
+ else:
1686
+ print("HF_TOKEN_READ not set. Cannot restore from backup. Starting fresh.")
1687
  else:
1688
+ user_count = len([k for k in visitor_data_cache if k != 'organization_details'])
1689
+ print(f"Successfully loaded data for {user_count} users from local file.")
1690
 
 
 
 
1691
  print("WARNING: The /admin route is NOT protected. Implement proper authentication for production.")
1692
+
1693
  if HF_TOKEN_WRITE:
1694
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
1695
  backup_thread.start()
1696
  print("Periodic backup thread started (every hour).")
1697
+ else:
1698
+ print("WARNING: HF_TOKEN_WRITE not set. Periodic backups to Hugging Face are disabled.")
1699
 
1700
  print("--- Server Ready ---")
1701
  app.run(host=HOST, port=PORT, debug=False)