Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
-
# -*- coding: utf-8 -*-
|
| 3 |
|
| 4 |
import os
|
| 5 |
from flask import Flask, request, Response, render_template_string, jsonify, redirect, url_for
|
|
@@ -469,6 +468,8 @@ ADMIN_TEMPLATE = """
|
|
| 469 |
.btn { padding: 12px 20px; font-size: 1em; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; transition: background-color 0.2s ease; }
|
| 470 |
.btn-primary { background-color: var(--admin-primary); color: #000; }
|
| 471 |
.btn-primary:hover { background-color: var(--admin-primary-dark); }
|
|
|
|
|
|
|
| 472 |
.user-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: var(--padding); margin-top: var(--padding); }
|
| 473 |
.user-card { background-color: var(--admin-card-bg); border-radius: var(--border-radius); padding: var(--padding); box-shadow: 0 4px 15px var(--admin-shadow); border: 1px solid var(--admin-border); display: flex; flex-direction: column; transition: transform 0.2s ease, box-shadow 0.2s ease; }
|
| 474 |
.user-card:hover { transform: translateY(-5px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08); }
|
|
@@ -479,12 +480,9 @@ ADMIN_TEMPLATE = """
|
|
| 479 |
.user-bonuses { text-align: center; margin-bottom: 1rem; }
|
| 480 |
.user-bonuses .label { font-size: 0.9em; color: var(--admin-secondary); }
|
| 481 |
.user-bonuses .amount { font-size: 1.8em; font-weight: 700; color: var(--admin-primary-dark); }
|
| 482 |
-
.user-actions { display: flex; gap: 0.5rem;
|
| 483 |
-
.btn-manage
|
| 484 |
-
.btn-manage { background-color: var(--admin-primary); color: #000; }
|
| 485 |
.btn-manage:hover { background-color: var(--admin-primary-dark); }
|
| 486 |
-
.btn-delete { background-color: var(--admin-danger); color: white; }
|
| 487 |
-
.btn-delete:hover { background-color: #c82333; }
|
| 488 |
.no-users { text-align: center; color: var(--admin-secondary); margin-top: 2rem; font-size: 1.1em; }
|
| 489 |
.modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.5); backdrop-filter: blur(5px); }
|
| 490 |
.modal-content { background-color: var(--admin-bg); margin: 10% auto; padding: var(--padding); border: 1px solid var(--admin-border); width: 90%; max-width: 600px; border-radius: var(--border-radius); position: relative; box-shadow: 0 8px 30px rgba(0,0,0,0.15); }
|
|
@@ -538,8 +536,10 @@ ADMIN_TEMPLATE = """
|
|
| 538 |
<div class="amount">{{ "%.2f"|format(user.bonuses|float) }}</div>
|
| 539 |
</div>
|
| 540 |
<div class="user-actions">
|
| 541 |
-
<button class="btn-manage" onclick='openTransactionModal({{ user|tojson }})'
|
| 542 |
-
|
|
|
|
|
|
|
| 543 |
</div>
|
| 544 |
</div>
|
| 545 |
{% endfor %}
|
|
@@ -761,28 +761,21 @@ ADMIN_TEMPLATE = """
|
|
| 761 |
}
|
| 762 |
}
|
| 763 |
|
| 764 |
-
async function deleteClient(
|
| 765 |
-
|
| 766 |
-
const userName = `${userData.first_name || ''} ${userData.last_name || ''}`.trim() || `@${userData.username || userData.phone_number}`;
|
| 767 |
-
|
| 768 |
-
if (!confirm(`Вы уверены, что хотите удалить клиента ${userName} (ID: ${userId})? Это действие необратимо.`)) {
|
| 769 |
return;
|
| 770 |
}
|
| 771 |
-
|
| 772 |
try {
|
| 773 |
const response = await fetch('/admin/delete_client', {
|
| 774 |
method: 'POST',
|
| 775 |
headers: { 'Content-Type': 'application/json' },
|
| 776 |
body: JSON.stringify({ user_id: userId })
|
| 777 |
});
|
| 778 |
-
|
| 779 |
const result = await response.json();
|
| 780 |
-
|
| 781 |
if (response.ok) {
|
| 782 |
-
alert('Клиент успешно удален.');
|
| 783 |
location.reload();
|
| 784 |
} else {
|
| 785 |
-
throw new Error(result.message || '
|
| 786 |
}
|
| 787 |
} catch (error) {
|
| 788 |
alert(`Ошибка: ${error.message}`);
|
|
@@ -944,38 +937,6 @@ def add_client():
|
|
| 944 |
logging.exception("Error in /admin/add_client endpoint")
|
| 945 |
return jsonify({"status": "error", "message": str(e)}), 500
|
| 946 |
|
| 947 |
-
@app.route('/admin/delete_client', methods=['POST'])
|
| 948 |
-
def delete_client():
|
| 949 |
-
try:
|
| 950 |
-
data = request.get_json()
|
| 951 |
-
user_id = data.get('user_id')
|
| 952 |
-
|
| 953 |
-
if not user_id:
|
| 954 |
-
return jsonify({"status": "error", "message": "User ID is required"}), 400
|
| 955 |
-
|
| 956 |
-
user_id_str = str(user_id)
|
| 957 |
-
|
| 958 |
-
with _data_lock:
|
| 959 |
-
all_data = load_visitor_data()
|
| 960 |
-
|
| 961 |
-
if user_id_str not in all_data:
|
| 962 |
-
return jsonify({"status": "error", "message": "User not found"}), 404
|
| 963 |
-
|
| 964 |
-
del all_data[user_id_str]
|
| 965 |
-
|
| 966 |
-
with open(DATA_FILE, 'w', encoding='utf-8') as f:
|
| 967 |
-
json.dump(all_data, f, ensure_ascii=False, indent=4)
|
| 968 |
-
|
| 969 |
-
logging.info(f"User {user_id_str} deleted. Data saved to {DATA_FILE}.")
|
| 970 |
-
|
| 971 |
-
upload_data_to_hf_async()
|
| 972 |
-
|
| 973 |
-
return jsonify({"status": "ok", "message": "Client deleted successfully"}), 200
|
| 974 |
-
|
| 975 |
-
except Exception as e:
|
| 976 |
-
logging.exception("Error in /admin/delete_client endpoint")
|
| 977 |
-
return jsonify({"status": "error", "message": str(e)}), 500
|
| 978 |
-
|
| 979 |
|
| 980 |
@app.route('/admin/add_transaction', methods=['POST'])
|
| 981 |
def add_transaction():
|
|
@@ -1034,6 +995,43 @@ def add_transaction():
|
|
| 1034 |
logging.exception("Error in /admin/add_transaction endpoint")
|
| 1035 |
return jsonify({"status": "error", "message": str(e)}), 500
|
| 1036 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1037 |
if __name__ == '__main__':
|
| 1038 |
print("--- DRUZHBA BONUS SYSTEM SERVER ---")
|
| 1039 |
print(f"Server starting on http://{HOST}:{PORT}")
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
|
|
|
| 2 |
|
| 3 |
import os
|
| 4 |
from flask import Flask, request, Response, render_template_string, jsonify, redirect, url_for
|
|
|
|
| 468 |
.btn { padding: 12px 20px; font-size: 1em; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; transition: background-color 0.2s ease; }
|
| 469 |
.btn-primary { background-color: var(--admin-primary); color: #000; }
|
| 470 |
.btn-primary:hover { background-color: var(--admin-primary-dark); }
|
| 471 |
+
.btn-delete { background-color: var(--admin-danger); color: white; }
|
| 472 |
+
.btn-delete:hover { background-color: #c82333; }
|
| 473 |
.user-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: var(--padding); margin-top: var(--padding); }
|
| 474 |
.user-card { background-color: var(--admin-card-bg); border-radius: var(--border-radius); padding: var(--padding); box-shadow: 0 4px 15px var(--admin-shadow); border: 1px solid var(--admin-border); display: flex; flex-direction: column; transition: transform 0.2s ease, box-shadow 0.2s ease; }
|
| 475 |
.user-card:hover { transform: translateY(-5px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08); }
|
|
|
|
| 480 |
.user-bonuses { text-align: center; margin-bottom: 1rem; }
|
| 481 |
.user-bonuses .label { font-size: 0.9em; color: var(--admin-secondary); }
|
| 482 |
.user-bonuses .amount { font-size: 1.8em; font-weight: 700; color: var(--admin-primary-dark); }
|
| 483 |
+
.user-actions { margin-top: auto; display: flex; flex-direction: column; gap: 0.5rem; }
|
| 484 |
+
.btn-manage { display: block; width: 100%; padding: 10px; background-color: var(--admin-primary); color: #000; border: none; border-radius: 8px; font-weight: 600; cursor: pointer; transition: background-color 0.2s; }
|
|
|
|
| 485 |
.btn-manage:hover { background-color: var(--admin-primary-dark); }
|
|
|
|
|
|
|
| 486 |
.no-users { text-align: center; color: var(--admin-secondary); margin-top: 2rem; font-size: 1.1em; }
|
| 487 |
.modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.5); backdrop-filter: blur(5px); }
|
| 488 |
.modal-content { background-color: var(--admin-bg); margin: 10% auto; padding: var(--padding); border: 1px solid var(--admin-border); width: 90%; max-width: 600px; border-radius: var(--border-radius); position: relative; box-shadow: 0 8px 30px rgba(0,0,0,0.15); }
|
|
|
|
| 536 |
<div class="amount">{{ "%.2f"|format(user.bonuses|float) }}</div>
|
| 537 |
</div>
|
| 538 |
<div class="user-actions">
|
| 539 |
+
<button class="btn-manage" onclick='openTransactionModal({{ user|tojson }})'>Управление бонусами</button>
|
| 540 |
+
{% if user.telegram_id == None %}
|
| 541 |
+
<button class="btn btn-delete" onclick='deleteClient("{{ user.id }}")'>Удалить клиента</button>
|
| 542 |
+
{% endif %}
|
| 543 |
</div>
|
| 544 |
</div>
|
| 545 |
{% endfor %}
|
|
|
|
| 761 |
}
|
| 762 |
}
|
| 763 |
|
| 764 |
+
async function deleteClient(userId) {
|
| 765 |
+
if (!confirm(`Вы уверены, что хотите удалить клиента с ID ${userId}? Это действие необратимо.`)) {
|
|
|
|
|
|
|
|
|
|
| 766 |
return;
|
| 767 |
}
|
|
|
|
| 768 |
try {
|
| 769 |
const response = await fetch('/admin/delete_client', {
|
| 770 |
method: 'POST',
|
| 771 |
headers: { 'Content-Type': 'application/json' },
|
| 772 |
body: JSON.stringify({ user_id: userId })
|
| 773 |
});
|
|
|
|
| 774 |
const result = await response.json();
|
|
|
|
| 775 |
if (response.ok) {
|
|
|
|
| 776 |
location.reload();
|
| 777 |
} else {
|
| 778 |
+
throw new Error(result.message || 'Не удалось удалить клиента.');
|
| 779 |
}
|
| 780 |
} catch (error) {
|
| 781 |
alert(`Ошибка: ${error.message}`);
|
|
|
|
| 937 |
logging.exception("Error in /admin/add_client endpoint")
|
| 938 |
return jsonify({"status": "error", "message": str(e)}), 500
|
| 939 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 940 |
|
| 941 |
@app.route('/admin/add_transaction', methods=['POST'])
|
| 942 |
def add_transaction():
|
|
|
|
| 995 |
logging.exception("Error in /admin/add_transaction endpoint")
|
| 996 |
return jsonify({"status": "error", "message": str(e)}), 500
|
| 997 |
|
| 998 |
+
@app.route('/admin/delete_client', methods=['POST'])
|
| 999 |
+
def delete_client():
|
| 1000 |
+
try:
|
| 1001 |
+
data = request.get_json()
|
| 1002 |
+
user_id = data.get('user_id')
|
| 1003 |
+
|
| 1004 |
+
if not user_id:
|
| 1005 |
+
return jsonify({"status": "error", "message": "User ID is required"}), 400
|
| 1006 |
+
|
| 1007 |
+
user_id_str = str(user_id)
|
| 1008 |
+
load_visitor_data()
|
| 1009 |
+
|
| 1010 |
+
with _data_lock:
|
| 1011 |
+
if user_id_str not in visitor_data_cache:
|
| 1012 |
+
return jsonify({"status": "error", "message": "User not found"}), 404
|
| 1013 |
+
|
| 1014 |
+
user_to_delete = visitor_data_cache[user_id_str]
|
| 1015 |
+
if user_to_delete.get('telegram_id') is not None:
|
| 1016 |
+
return jsonify({"status": "error", "message": "Cannot delete a Telegram-linked user"}), 403
|
| 1017 |
+
|
| 1018 |
+
del visitor_data_cache[user_id_str]
|
| 1019 |
+
|
| 1020 |
+
try:
|
| 1021 |
+
with open(DATA_FILE, 'w', encoding='utf-8') as f:
|
| 1022 |
+
json.dump(visitor_data_cache, f, ensure_ascii=False, indent=4)
|
| 1023 |
+
logging.info(f"User {user_id_str} deleted. Data saved to {DATA_FILE}.")
|
| 1024 |
+
upload_data_to_hf_async()
|
| 1025 |
+
except Exception as e:
|
| 1026 |
+
logging.error(f"Error saving data after deletion: {e}")
|
| 1027 |
+
return jsonify({"status": "error", "message": "Failed to save data after deletion"}), 500
|
| 1028 |
+
|
| 1029 |
+
return jsonify({"status": "ok", "message": "Client deleted successfully"}), 200
|
| 1030 |
+
|
| 1031 |
+
except Exception as e:
|
| 1032 |
+
logging.exception("Error in /admin/delete_client endpoint")
|
| 1033 |
+
return jsonify({"status": "error", "message": str(e)}), 500
|
| 1034 |
+
|
| 1035 |
if __name__ == '__main__':
|
| 1036 |
print("--- DRUZHBA BONUS SYSTEM SERVER ---")
|
| 1037 |
print(f"Server starting on http://{HOST}:{PORT}")
|