Update app.py
Browse files
app.py
CHANGED
|
@@ -4,7 +4,6 @@ import os
|
|
| 4 |
import logging
|
| 5 |
import threading
|
| 6 |
import time
|
| 7 |
-
import requests
|
| 8 |
from datetime import datetime
|
| 9 |
from huggingface_hub import HfApi, hf_hub_download
|
| 10 |
from huggingface_hub.utils import RepositoryNotFoundError
|
|
@@ -13,60 +12,47 @@ from werkzeug.utils import secure_filename
|
|
| 13 |
app = Flask(__name__)
|
| 14 |
app.secret_key = 'your_unique_secret_key_12345' # Уникальный секретный ключ
|
| 15 |
DATA_FILE = 'data_detobuv.json'
|
| 16 |
-
USERS_FILE = '
|
|
|
|
| 17 |
|
| 18 |
# Настройки Hugging Face
|
| 19 |
REPO_ID = "Kgshop/clients"
|
| 20 |
HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
|
| 21 |
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
|
| 22 |
|
| 23 |
-
#
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
# API для получения актуальных курсов валют
|
| 27 |
-
EXCHANGE_RATE_API_KEY = "a1bbe02a37d9584ab12ffc1f" # Замените на ваш ключ от ExchangeRate-API
|
| 28 |
-
EXCHANGE_RATE_API_URL = f"https://v6.exchangerate-api.com/v6/{EXCHANGE_RATE_API_KEY}/latest/USD"
|
| 29 |
|
| 30 |
# Поддерживаемые валюты
|
| 31 |
CURRENCIES = {
|
| 32 |
'USD': 'Доллар США ($)',
|
| 33 |
-
'KGS': 'Кыргызский сом (с)'
|
| 34 |
-
'RUB': 'Российский рубль (₽)',
|
| 35 |
-
'KZT': 'Казахский тенге (₸)'
|
| 36 |
}
|
| 37 |
|
| 38 |
# Настройка логирования
|
| 39 |
logging.basicConfig(level=logging.DEBUG)
|
| 40 |
|
| 41 |
-
def
|
| 42 |
-
"""
|
| 43 |
try:
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
'KGS': data['conversion_rates']['KGS'],
|
| 50 |
-
'RUB': data['conversion_rates']['RUB'],
|
| 51 |
-
'KZT': data['conversion_rates']['KZT']
|
| 52 |
-
}
|
| 53 |
-
logging.info("Курсы валют успешно обновлены из API")
|
| 54 |
-
return rates
|
| 55 |
-
else:
|
| 56 |
-
raise Exception("Ошибка API: " + data['error-type'])
|
| 57 |
-
except Exception as e:
|
| 58 |
-
logging.error(f"Не удалось получить курсы валют: {e}")
|
| 59 |
-
# Резервные фиксированные курсы в случае сбоя API
|
| 60 |
-
return {
|
| 61 |
-
'USD': 1.0,
|
| 62 |
-
'KGS': 89.0,
|
| 63 |
-
'RUB': 97.0,
|
| 64 |
-
'KZT': 480.0
|
| 65 |
-
}
|
| 66 |
|
| 67 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
"""Конвертация цены из USD в указанную валюту."""
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
def load_data():
|
| 72 |
try:
|
|
@@ -157,7 +143,6 @@ def catalog():
|
|
| 157 |
products = data['products']
|
| 158 |
categories = data['categories']
|
| 159 |
is_authenticated = 'user' in session
|
| 160 |
-
rates = get_exchange_rates()
|
| 161 |
selected_currency = session.get('currency', 'USD') if is_authenticated else 'USD'
|
| 162 |
|
| 163 |
catalog_html = '''
|
|
@@ -166,7 +151,7 @@ def catalog():
|
|
| 166 |
<head>
|
| 167 |
<meta charset="UTF-8">
|
| 168 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 169 |
-
<title>Детская обувь
|
| 170 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 171 |
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
| 172 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
|
@@ -505,7 +490,7 @@ def catalog():
|
|
| 505 |
<i class="fas fa-moon"></i>
|
| 506 |
</button>
|
| 507 |
</div>
|
| 508 |
-
<div class="store-address"
|
| 509 |
<div class="filters-container">
|
| 510 |
<button class="category-filter active" data-category="all">Все категории</button>
|
| 511 |
{% for category in categories %}
|
|
@@ -530,7 +515,7 @@ def catalog():
|
|
| 530 |
{% endif %}
|
| 531 |
<h2>{{ product['name'] }}</h2>
|
| 532 |
{% if is_authenticated %}
|
| 533 |
-
<div class="product-price">{{ convert_price(product['price'], selected_currency
|
| 534 |
{% else %}
|
| 535 |
<div class="product-price">Цена доступна после входа</div>
|
| 536 |
{% endif %}
|
|
@@ -586,6 +571,7 @@ def catalog():
|
|
| 586 |
const products = {{ products|tojson }};
|
| 587 |
let selectedProductIndex = null;
|
| 588 |
const selectedCurrency = '{{ selected_currency }}';
|
|
|
|
| 589 |
|
| 590 |
function toggleTheme() {
|
| 591 |
document.body.classList.toggle('dark-mode');
|
|
@@ -707,7 +693,8 @@ def catalog():
|
|
| 707 |
let total = 0;
|
| 708 |
|
| 709 |
cartContent.innerHTML = cart.length === 0 ? '<p>Корзина пуста</p>' : cart.map(item => {
|
| 710 |
-
const
|
|
|
|
| 711 |
total += itemTotal;
|
| 712 |
return `
|
| 713 |
<div class="cart-item">
|
|
@@ -715,7 +702,7 @@ def catalog():
|
|
| 715 |
${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${item.photo}" alt="${item.name}">` : ''}
|
| 716 |
<div>
|
| 717 |
<strong>${item.name}</strong>
|
| 718 |
-
<p>${
|
| 719 |
</div>
|
| 720 |
</div>
|
| 721 |
<span>${itemTotal.toFixed(2)} ${selectedCurrency}</span>
|
|
@@ -736,9 +723,10 @@ def catalog():
|
|
| 736 |
let total = 0;
|
| 737 |
let orderText = "Заказ:%0A";
|
| 738 |
cart.forEach((item, index) => {
|
| 739 |
-
const
|
|
|
|
| 740 |
total += itemTotal;
|
| 741 |
-
orderText += `${index + 1}. ${item.name} - ${
|
| 742 |
});
|
| 743 |
orderText += `Итого: ${total.toFixed(2)} ${selectedCurrency}%0A`;
|
| 744 |
orderText += `Страна: {{ session.get('country', 'Не указана') }}%0A`;
|
|
@@ -785,8 +773,8 @@ def catalog():
|
|
| 785 |
'''
|
| 786 |
return render_template_string(catalog_html, products=products, categories=categories,
|
| 787 |
repo_id=REPO_ID, is_authenticated=is_authenticated,
|
| 788 |
-
|
| 789 |
-
|
| 790 |
selected_currency=selected_currency, currencies=CURRENCIES)
|
| 791 |
|
| 792 |
@app.route('/product/<int:index>')
|
|
@@ -794,7 +782,6 @@ def product_detail(index):
|
|
| 794 |
data = load_data()
|
| 795 |
products = data['products']
|
| 796 |
is_authenticated = 'user' in session
|
| 797 |
-
rates = get_exchange_rates()
|
| 798 |
selected_currency = session.get('currency', 'USD') if is_authenticated else 'USD'
|
| 799 |
try:
|
| 800 |
product = products[index]
|
|
@@ -827,7 +814,7 @@ def product_detail(index):
|
|
| 827 |
</div>
|
| 828 |
<p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
|
| 829 |
{% if is_authenticated %}
|
| 830 |
-
<p><strong>Цена:</strong> {{ convert_price(product['price'], selected_currency
|
| 831 |
{% else %}
|
| 832 |
<p><strong>Цена:</strong> Доступна после входа</p>
|
| 833 |
{% endif %}
|
|
@@ -837,7 +824,7 @@ def product_detail(index):
|
|
| 837 |
'''
|
| 838 |
return render_template_string(detail_html, product=product, repo_id=REPO_ID,
|
| 839 |
is_authenticated=is_authenticated, convert_price=convert_price,
|
| 840 |
-
|
| 841 |
|
| 842 |
@app.route('/set_currency', methods=['POST'])
|
| 843 |
def set_currency():
|
|
@@ -852,6 +839,8 @@ def register():
|
|
| 852 |
if request.method == 'POST':
|
| 853 |
login = request.form.get('login')
|
| 854 |
password = request.form.get('password')
|
|
|
|
|
|
|
| 855 |
country = request.form.get('country')
|
| 856 |
city = request.form.get('city')
|
| 857 |
purchase_type = request.form.get('purchase_type')
|
|
@@ -868,6 +857,8 @@ def register():
|
|
| 868 |
|
| 869 |
users[login] = {
|
| 870 |
'password': password,
|
|
|
|
|
|
|
| 871 |
'country': country,
|
| 872 |
'city': city,
|
| 873 |
'purchase_type': purchase_type
|
|
@@ -938,6 +929,10 @@ def register():
|
|
| 938 |
<input type="text" name="login" required>
|
| 939 |
<label>Пароль:</label>
|
| 940 |
<input type="password" name="password" required>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 941 |
<label>Страна:</label>
|
| 942 |
<input type="text" name="country" required>
|
| 943 |
<label>Город:</label>
|
|
@@ -1079,6 +1074,8 @@ def admin():
|
|
| 1079 |
data = load_data()
|
| 1080 |
products = data['products']
|
| 1081 |
categories = data['categories']
|
|
|
|
|
|
|
| 1082 |
|
| 1083 |
if request.method == 'POST':
|
| 1084 |
action = request.form.get('action')
|
|
@@ -1191,6 +1188,18 @@ def admin():
|
|
| 1191 |
del products[index]
|
| 1192 |
save_data(data)
|
| 1193 |
return redirect(url_for('admin'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1194 |
|
| 1195 |
admin_html = '''
|
| 1196 |
<!DOCTYPE html>
|
|
@@ -1268,11 +1277,11 @@ def admin():
|
|
| 1268 |
background-color: #dc2626;
|
| 1269 |
box-shadow: 0 4px 15px rgba(220, 38, 38, 0.4);
|
| 1270 |
}
|
| 1271 |
-
.product-list, .category-list {
|
| 1272 |
display: grid;
|
| 1273 |
gap: 20px;
|
| 1274 |
}
|
| 1275 |
-
.product-item, .category-item {
|
| 1276 |
background: #fff;
|
| 1277 |
padding: 20px;
|
| 1278 |
border-radius: 15px;
|
|
@@ -1302,6 +1311,15 @@ def admin():
|
|
| 1302 |
<div class="header">
|
| 1303 |
<h1>Админ-панель</h1>
|
| 1304 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1305 |
<h1>Добавление товара</h1>
|
| 1306 |
<form method="POST" enctype="multipart/form-data">
|
| 1307 |
<input type="hidden" name="action" value="add">
|
|
@@ -1366,7 +1384,7 @@ def admin():
|
|
| 1366 |
<div class="product-item">
|
| 1367 |
<h3>{{ product['name'] }}</h3>
|
| 1368 |
<p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
|
| 1369 |
-
<p><strong>Цена:</strong> {{ product['price'] }} USD</p>
|
| 1370 |
<p><strong>Описание:</strong> {{ product['description'] }}</p>
|
| 1371 |
<p><strong>Цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
|
| 1372 |
{% if product.get('photos') and product['photos']|length > 0 %}
|
|
@@ -1418,6 +1436,24 @@ def admin():
|
|
| 1418 |
</div>
|
| 1419 |
{% endfor %}
|
| 1420 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1421 |
</div>
|
| 1422 |
<script>
|
| 1423 |
function addColorInput(containerId = 'color-inputs') {
|
|
@@ -1431,7 +1467,9 @@ def admin():
|
|
| 1431 |
</body>
|
| 1432 |
</html>
|
| 1433 |
'''
|
| 1434 |
-
return render_template_string(admin_html, products=products, categories=categories,
|
|
|
|
|
|
|
| 1435 |
|
| 1436 |
@app.route('/backup', methods=['POST'])
|
| 1437 |
def backup():
|
|
|
|
| 4 |
import logging
|
| 5 |
import threading
|
| 6 |
import time
|
|
|
|
| 7 |
from datetime import datetime
|
| 8 |
from huggingface_hub import HfApi, hf_hub_download
|
| 9 |
from huggingface_hub.utils import RepositoryNotFoundError
|
|
|
|
| 12 |
app = Flask(__name__)
|
| 13 |
app.secret_key = 'your_unique_secret_key_12345' # Уникальный секретный ключ
|
| 14 |
DATA_FILE = 'data_detobuv.json'
|
| 15 |
+
USERS_FILE = 'users.json'
|
| 16 |
+
CONFIG_FILE = 'config.json' # Файл для хранения курса KGS к USD
|
| 17 |
|
| 18 |
# Настройки Hugging Face
|
| 19 |
REPO_ID = "Kgshop/clients"
|
| 20 |
HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
|
| 21 |
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
|
| 22 |
|
| 23 |
+
# Адреса магазина
|
| 24 |
+
WHOLESALE_ADDRESS = "Дордой, рынок Кербен, 9 ряд, 06 бутик" # Опт
|
| 25 |
+
RETAIL_ADDRESS = "Дордой Мир Обуви, номер 150" # Розница
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
# Поддерживаемые валюты
|
| 28 |
CURRENCIES = {
|
| 29 |
'USD': 'Доллар США ($)',
|
| 30 |
+
'KGS': 'Кыргызский сом (с)'
|
|
|
|
|
|
|
| 31 |
}
|
| 32 |
|
| 33 |
# Настройка логирования
|
| 34 |
logging.basicConfig(level=logging.DEBUG)
|
| 35 |
|
| 36 |
+
def load_config():
|
| 37 |
+
"""Загрузка конфигурации (курс KGS к USD)."""
|
| 38 |
try:
|
| 39 |
+
with open(CONFIG_FILE, 'r', encoding='utf-8') as file:
|
| 40 |
+
config = json.load(file)
|
| 41 |
+
return config.get('kgs_to_usd', 89.0) # Значение по умолчанию
|
| 42 |
+
except (FileNotFoundError, json.JSONDecodeError):
|
| 43 |
+
return 89.0 # Резервное значение
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
+
def save_config(kgs_to_usd):
|
| 46 |
+
"""Сохранение конфигурации."""
|
| 47 |
+
with open(CONFIG_FILE, 'w', encoding='utf-8') as file:
|
| 48 |
+
json.dump({'kgs_to_usd': kgs_to_usd}, file, ensure_ascii=False, indent=4)
|
| 49 |
+
|
| 50 |
+
def convert_price(price_usd, currency):
|
| 51 |
"""Конвертация цены из USD в указанную валюту."""
|
| 52 |
+
kgs_to_usd = load_config()
|
| 53 |
+
if currency == 'KGS':
|
| 54 |
+
return round(price_usd * kgs_to_usd, 2)
|
| 55 |
+
return round(price_usd, 2)
|
| 56 |
|
| 57 |
def load_data():
|
| 58 |
try:
|
|
|
|
| 143 |
products = data['products']
|
| 144 |
categories = data['categories']
|
| 145 |
is_authenticated = 'user' in session
|
|
|
|
| 146 |
selected_currency = session.get('currency', 'USD') if is_authenticated else 'USD'
|
| 147 |
|
| 148 |
catalog_html = '''
|
|
|
|
| 151 |
<head>
|
| 152 |
<meta charset="UTF-8">
|
| 153 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 154 |
+
<title>Детская обувь оптом и в розницу</title>
|
| 155 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 156 |
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
| 157 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
|
|
|
| 490 |
<i class="fas fa-moon"></i>
|
| 491 |
</button>
|
| 492 |
</div>
|
| 493 |
+
<div class="store-address">Опт: {{ wholesale_address }} | ��озница: {{ retail_address }}</div>
|
| 494 |
<div class="filters-container">
|
| 495 |
<button class="category-filter active" data-category="all">Все категории</button>
|
| 496 |
{% for category in categories %}
|
|
|
|
| 515 |
{% endif %}
|
| 516 |
<h2>{{ product['name'] }}</h2>
|
| 517 |
{% if is_authenticated %}
|
| 518 |
+
<div class="product-price">{{ convert_price(product['price'], selected_currency) }} {{ selected_currency }}</div>
|
| 519 |
{% else %}
|
| 520 |
<div class="product-price">Цена доступна после входа</div>
|
| 521 |
{% endif %}
|
|
|
|
| 571 |
const products = {{ products|tojson }};
|
| 572 |
let selectedProductIndex = null;
|
| 573 |
const selectedCurrency = '{{ selected_currency }}';
|
| 574 |
+
const kgsToUsd = {{ load_config()|tojson }};
|
| 575 |
|
| 576 |
function toggleTheme() {
|
| 577 |
document.body.classList.toggle('dark-mode');
|
|
|
|
| 693 |
let total = 0;
|
| 694 |
|
| 695 |
cartContent.innerHTML = cart.length === 0 ? '<p>Корзина пуста</p>' : cart.map(item => {
|
| 696 |
+
const itemPrice = selectedCurrency === 'KGS' ? (item.price * kgsToUsd) : item.price;
|
| 697 |
+
const itemTotal = itemPrice * item.quantity;
|
| 698 |
total += itemTotal;
|
| 699 |
return `
|
| 700 |
<div class="cart-item">
|
|
|
|
| 702 |
${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${item.photo}" alt="${item.name}">` : ''}
|
| 703 |
<div>
|
| 704 |
<strong>${item.name}</strong>
|
| 705 |
+
<p>${itemPrice.toFixed(2)} ${selectedCurrency} × ${item.quantity} (Цвет: ${item.color})</p>
|
| 706 |
</div>
|
| 707 |
</div>
|
| 708 |
<span>${itemTotal.toFixed(2)} ${selectedCurrency}</span>
|
|
|
|
| 723 |
let total = 0;
|
| 724 |
let orderText = "Заказ:%0A";
|
| 725 |
cart.forEach((item, index) => {
|
| 726 |
+
const itemPrice = selectedCurrency === 'KGS' ? (item.price * kgsToUsd) : item.price;
|
| 727 |
+
const itemTotal = itemPrice * item.quantity;
|
| 728 |
total += itemTotal;
|
| 729 |
+
orderText += `${index + 1}. ${item.name} - ${itemPrice.toFixed(2)} ${selectedCurrency} × ${item.quantity} (Цвет: ${item.color})%0A`;
|
| 730 |
});
|
| 731 |
orderText += `Итого: ${total.toFixed(2)} ${selectedCurrency}%0A`;
|
| 732 |
orderText += `Страна: {{ session.get('country', 'Не указана') }}%0A`;
|
|
|
|
| 773 |
'''
|
| 774 |
return render_template_string(catalog_html, products=products, categories=categories,
|
| 775 |
repo_id=REPO_ID, is_authenticated=is_authenticated,
|
| 776 |
+
wholesale_address=WHOLESALE_ADDRESS, retail_address=RETAIL_ADDRESS,
|
| 777 |
+
session=session, convert_price=convert_price,
|
| 778 |
selected_currency=selected_currency, currencies=CURRENCIES)
|
| 779 |
|
| 780 |
@app.route('/product/<int:index>')
|
|
|
|
| 782 |
data = load_data()
|
| 783 |
products = data['products']
|
| 784 |
is_authenticated = 'user' in session
|
|
|
|
| 785 |
selected_currency = session.get('currency', 'USD') if is_authenticated else 'USD'
|
| 786 |
try:
|
| 787 |
product = products[index]
|
|
|
|
| 814 |
</div>
|
| 815 |
<p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
|
| 816 |
{% if is_authenticated %}
|
| 817 |
+
<p><strong>Цена:</strong> {{ convert_price(product['price'], selected_currency) }} {{ selected_currency }}</p>
|
| 818 |
{% else %}
|
| 819 |
<p><strong>Цена:</strong> Доступна после входа</p>
|
| 820 |
{% endif %}
|
|
|
|
| 824 |
'''
|
| 825 |
return render_template_string(detail_html, product=product, repo_id=REPO_ID,
|
| 826 |
is_authenticated=is_authenticated, convert_price=convert_price,
|
| 827 |
+
selected_currency=selected_currency)
|
| 828 |
|
| 829 |
@app.route('/set_currency', methods=['POST'])
|
| 830 |
def set_currency():
|
|
|
|
| 839 |
if request.method == 'POST':
|
| 840 |
login = request.form.get('login')
|
| 841 |
password = request.form.get('password')
|
| 842 |
+
first_name = request.form.get('first_name')
|
| 843 |
+
last_name = request.form.get('last_name')
|
| 844 |
country = request.form.get('country')
|
| 845 |
city = request.form.get('city')
|
| 846 |
purchase_type = request.form.get('purchase_type')
|
|
|
|
| 857 |
|
| 858 |
users[login] = {
|
| 859 |
'password': password,
|
| 860 |
+
'first_name': first_name,
|
| 861 |
+
'last_name': last_name,
|
| 862 |
'country': country,
|
| 863 |
'city': city,
|
| 864 |
'purchase_type': purchase_type
|
|
|
|
| 929 |
<input type="text" name="login" required>
|
| 930 |
<label>Пароль:</label>
|
| 931 |
<input type="password" name="password" required>
|
| 932 |
+
<label>Имя:</label>
|
| 933 |
+
<input type="text" name="first_name" required>
|
| 934 |
+
<label>Фамилия:</label>
|
| 935 |
+
<input type="text" name="last_name" required>
|
| 936 |
<label>Страна:</label>
|
| 937 |
<input type="text" name="country" required>
|
| 938 |
<label>Город:</label>
|
|
|
|
| 1074 |
data = load_data()
|
| 1075 |
products = data['products']
|
| 1076 |
categories = data['categories']
|
| 1077 |
+
users = load_users()
|
| 1078 |
+
kgs_to_usd = load_config()
|
| 1079 |
|
| 1080 |
if request.method == 'POST':
|
| 1081 |
action = request.form.get('action')
|
|
|
|
| 1188 |
del products[index]
|
| 1189 |
save_data(data)
|
| 1190 |
return redirect(url_for('admin'))
|
| 1191 |
+
|
| 1192 |
+
elif action == 'set_exchange_rate':
|
| 1193 |
+
kgs_to_usd = float(request.form.get('kgs_to_usd').replace(',', '.'))
|
| 1194 |
+
save_config(kgs_to_usd)
|
| 1195 |
+
return redirect(url_for('admin'))
|
| 1196 |
+
|
| 1197 |
+
elif action == 'delete_user':
|
| 1198 |
+
login = request.form.get('login')
|
| 1199 |
+
if login in users:
|
| 1200 |
+
del users[login]
|
| 1201 |
+
save_users(users)
|
| 1202 |
+
return redirect(url_for('admin'))
|
| 1203 |
|
| 1204 |
admin_html = '''
|
| 1205 |
<!DOCTYPE html>
|
|
|
|
| 1277 |
background-color: #dc2626;
|
| 1278 |
box-shadow: 0 4px 15px rgba(220, 38, 38, 0.4);
|
| 1279 |
}
|
| 1280 |
+
.product-list, .category-list, .user-list {
|
| 1281 |
display: grid;
|
| 1282 |
gap: 20px;
|
| 1283 |
}
|
| 1284 |
+
.product-item, .category-item, .user-item {
|
| 1285 |
background: #fff;
|
| 1286 |
padding: 20px;
|
| 1287 |
border-radius: 15px;
|
|
|
|
| 1311 |
<div class="header">
|
| 1312 |
<h1>Админ-панель</h1>
|
| 1313 |
</div>
|
| 1314 |
+
|
| 1315 |
+
<h1>Установка курса KGS к USD</h1>
|
| 1316 |
+
<form method="POST">
|
| 1317 |
+
<input type="hidden" name="action" value="set_exchange_rate">
|
| 1318 |
+
<label>Курс KGS к USD (1 USD = ? KGS):</label>
|
| 1319 |
+
<input type="number" name="kgs_to_usd" step="0.01" value="{{ kgs_to_usd }}" required>
|
| 1320 |
+
<button type="submit">Сохранить</button>
|
| 1321 |
+
</form>
|
| 1322 |
+
|
| 1323 |
<h1>Добавление товара</h1>
|
| 1324 |
<form method="POST" enctype="multipart/form-data">
|
| 1325 |
<input type="hidden" name="action" value="add">
|
|
|
|
| 1384 |
<div class="product-item">
|
| 1385 |
<h3>{{ product['name'] }}</h3>
|
| 1386 |
<p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
|
| 1387 |
+
<p><strong>Цена:</strong> {{ product['price'] }} USD ({{ convert_price(product['price'], 'KGS') }} KGS)</p>
|
| 1388 |
<p><strong>Описание:</strong> {{ product['description'] }}</p>
|
| 1389 |
<p><strong>Цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
|
| 1390 |
{% if product.get('photos') and product['photos']|length > 0 %}
|
|
|
|
| 1436 |
</div>
|
| 1437 |
{% endfor %}
|
| 1438 |
</div>
|
| 1439 |
+
|
| 1440 |
+
<h2>Список пользователей</h2>
|
| 1441 |
+
<div class="user-list">
|
| 1442 |
+
{% for login, user in users.items() %}
|
| 1443 |
+
<div class="user-item">
|
| 1444 |
+
<p><strong>Логин:</strong> {{ login }}</p>
|
| 1445 |
+
<p><strong>Имя:</strong> {{ user['first_name'] }}</p>
|
| 1446 |
+
<p><strong>Фlastname:</strong> {{ user['last_name'] }}</p>
|
| 1447 |
+
<p><strong>Страна:</strong> {{ user['country'] }}</p>
|
| 1448 |
+
<p><strong>Город:</strong> {{ user['city'] }}</p>
|
| 1449 |
+
<form method="POST">
|
| 1450 |
+
<input type="hidden" name="action" value="delete_user">
|
| 1451 |
+
<input type="hidden" name="login" value="{{ login }}">
|
| 1452 |
+
<button type="submit" class="delete-button">Удалить</button>
|
| 1453 |
+
</form>
|
| 1454 |
+
</div>
|
| 1455 |
+
{% endfor %}
|
| 1456 |
+
</div>
|
| 1457 |
</div>
|
| 1458 |
<script>
|
| 1459 |
function addColorInput(containerId = 'color-inputs') {
|
|
|
|
| 1467 |
</body>
|
| 1468 |
</html>
|
| 1469 |
'''
|
| 1470 |
+
return render_template_string(admin_html, products=products, categories=categories,
|
| 1471 |
+
repo_id=REPO_ID, users=users, kgs_to_usd=kgs_to_usd,
|
| 1472 |
+
convert_price=convert_price)
|
| 1473 |
|
| 1474 |
@app.route('/backup', methods=['POST'])
|
| 1475 |
def backup():
|