Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from flask import Flask, render_template_string, request, redirect, url_for
|
| 2 |
import json
|
| 3 |
import os
|
| 4 |
import logging
|
|
@@ -8,17 +8,20 @@ from datetime import datetime
|
|
| 8 |
from huggingface_hub import HfApi, hf_hub_download
|
| 9 |
from huggingface_hub.utils import RepositoryNotFoundError
|
| 10 |
from werkzeug.utils import secure_filename
|
|
|
|
| 11 |
|
| 12 |
app = Flask(__name__)
|
|
|
|
| 13 |
DATA_FILE = 'data_detobuv.json'
|
|
|
|
| 14 |
|
| 15 |
# Настройки Hugging Face
|
| 16 |
REPO_ID = "Kgshop/clients"
|
| 17 |
HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
|
| 18 |
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
|
| 19 |
|
| 20 |
-
#
|
| 21 |
-
|
| 22 |
|
| 23 |
# Настройка логирования
|
| 24 |
logging.basicConfig(level=logging.DEBUG)
|
|
@@ -55,6 +58,22 @@ def save_data(data):
|
|
| 55 |
logging.error(f"Ошибка при сохранении данных: {e}")
|
| 56 |
raise
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
def upload_db_to_hf():
|
| 59 |
try:
|
| 60 |
api = HfApi()
|
|
@@ -98,6 +117,7 @@ def catalog():
|
|
| 98 |
data = load_data()
|
| 99 |
products = data['products']
|
| 100 |
categories = data['categories']
|
|
|
|
| 101 |
|
| 102 |
catalog_html = '''
|
| 103 |
<!DOCTYPE html>
|
|
@@ -105,7 +125,7 @@ def catalog():
|
|
| 105 |
<head>
|
| 106 |
<meta charset="UTF-8">
|
| 107 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 108 |
-
<title>Детская обувь оптом
|
| 109 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 110 |
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
| 111 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
|
@@ -138,22 +158,21 @@ def catalog():
|
|
| 138 |
padding: 15px 0;
|
| 139 |
border-bottom: 1px solid #e2e8f0;
|
| 140 |
}
|
| 141 |
-
.header-logo {
|
| 142 |
-
width: 60px;
|
| 143 |
-
height: 60px;
|
| 144 |
-
border-radius: 50%;
|
| 145 |
-
object-fit: cover;
|
| 146 |
-
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
| 147 |
-
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
| 148 |
-
}
|
| 149 |
-
.header-logo:hover {
|
| 150 |
-
transform: scale(1.1);
|
| 151 |
-
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
| 152 |
-
}
|
| 153 |
.header h1 {
|
| 154 |
font-size: 1.5rem;
|
| 155 |
font-weight: 600;
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
}
|
| 158 |
.theme-toggle {
|
| 159 |
background: none;
|
|
@@ -407,13 +426,27 @@ def catalog():
|
|
| 407 |
background-color: #059669;
|
| 408 |
box-shadow: 0 4px 15px rgba(5, 150, 105, 0.4);
|
| 409 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
</style>
|
| 411 |
</head>
|
| 412 |
<body>
|
| 413 |
<div class="container">
|
| 414 |
<div class="header">
|
| 415 |
-
<img src="''' + LOGO_URL + '''" alt="Logo" class="header-logo">
|
| 416 |
<h1>Каталог</h1>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 417 |
<button class="theme-toggle" onclick="toggleTheme()">
|
| 418 |
<i class="fas fa-moon"></i>
|
| 419 |
</button>
|
|
@@ -441,13 +474,20 @@ def catalog():
|
|
| 441 |
</div>
|
| 442 |
{% endif %}
|
| 443 |
<h2>{{ product['name'] }}</h2>
|
|
|
|
| 444 |
<div class="product-price">{{ product['price'] }} с</div>
|
|
|
|
|
|
|
|
|
|
| 445 |
<p class="product-description">{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}</p>
|
| 446 |
<button class="product-button" onclick="openModal({{ loop.index0 }})">Подробнее</button>
|
|
|
|
| 447 |
<button class="product-button add-to-cart" onclick="openQuantityModal({{ loop.index0 }})">В корзину</button>
|
|
|
|
| 448 |
</div>
|
| 449 |
{% endfor %}
|
| 450 |
</div>
|
|
|
|
| 451 |
</div>
|
| 452 |
|
| 453 |
<!-- Product Modal -->
|
|
@@ -674,12 +714,15 @@ def catalog():
|
|
| 674 |
</body>
|
| 675 |
</html>
|
| 676 |
'''
|
| 677 |
-
return render_template_string(catalog_html, products=products, categories=categories,
|
|
|
|
|
|
|
| 678 |
|
| 679 |
@app.route('/product/<int:index>')
|
| 680 |
def product_detail(index):
|
| 681 |
data = load_data()
|
| 682 |
products = data['products']
|
|
|
|
| 683 |
try:
|
| 684 |
product = products[index]
|
| 685 |
except IndexError:
|
|
@@ -710,12 +753,209 @@ def product_detail(index):
|
|
| 710 |
<div class="swiper-button-prev"></div>
|
| 711 |
</div>
|
| 712 |
<p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
|
|
|
|
| 713 |
<p><strong>Цена:</strong> {{ product['price'] }} с</p>
|
|
|
|
|
|
|
|
|
|
| 714 |
<p><strong>Описание:</strong> {{ product['description'] }}</p>
|
| 715 |
<p><strong>Доступные цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
|
| 716 |
</div>
|
| 717 |
'''
|
| 718 |
-
return render_template_string(detail_html, product=product, repo_id=REPO_ID)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 719 |
|
| 720 |
@app.route('/admin', methods=['GET', 'POST'])
|
| 721 |
def admin():
|
|
@@ -753,7 +993,7 @@ def admin():
|
|
| 753 |
photos_list = []
|
| 754 |
|
| 755 |
if photos_files:
|
| 756 |
-
for photo in photos_files[:10]:
|
| 757 |
if photo and photo.filename:
|
| 758 |
photo_filename = secure_filename(photo.filename)
|
| 759 |
uploads_dir = 'uploads'
|
|
@@ -800,7 +1040,7 @@ def admin():
|
|
| 800 |
|
| 801 |
if photos_files and any(photo.filename for photo in photos_files):
|
| 802 |
new_photos_list = []
|
| 803 |
-
for photo in photos_files[:10]:
|
| 804 |
if photo and photo.filename:
|
| 805 |
photo_filename = secure_filename(photo.filename)
|
| 806 |
uploads_dir = 'uploads'
|
|
@@ -855,24 +1095,9 @@ def admin():
|
|
| 855 |
margin: 0 auto;
|
| 856 |
}
|
| 857 |
.header {
|
| 858 |
-
display: flex;
|
| 859 |
-
align-items: center;
|
| 860 |
padding: 15px 0;
|
| 861 |
border-bottom: 1px solid #e2e8f0;
|
| 862 |
}
|
| 863 |
-
.header-logo {
|
| 864 |
-
width: 60px;
|
| 865 |
-
height: 60px;
|
| 866 |
-
border-radius: 50%;
|
| 867 |
-
object-fit: cover;
|
| 868 |
-
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
| 869 |
-
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
| 870 |
-
margin-right: 15px;
|
| 871 |
-
}
|
| 872 |
-
.header-logo:hover {
|
| 873 |
-
transform: scale(1.1);
|
| 874 |
-
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
| 875 |
-
}
|
| 876 |
h1, h2 {
|
| 877 |
font-weight: 600;
|
| 878 |
margin-bottom: 20px;
|
|
@@ -958,7 +1183,6 @@ def admin():
|
|
| 958 |
<body>
|
| 959 |
<div class="container">
|
| 960 |
<div class="header">
|
| 961 |
-
<img src="''' + LOGO_URL + '''" alt="Logo" class="header-logo">
|
| 962 |
<h1>Админ-панель</h1>
|
| 963 |
</div>
|
| 964 |
<h1>Добавление товара</h1>
|
|
|
|
| 1 |
+
from flask import Flask, render_template_string, request, redirect, url_for, session
|
| 2 |
import json
|
| 3 |
import os
|
| 4 |
import logging
|
|
|
|
| 8 |
from huggingface_hub import HfApi, hf_hub_download
|
| 9 |
from huggingface_hub.utils import RepositoryNotFoundError
|
| 10 |
from werkzeug.utils import secure_filename
|
| 11 |
+
import hashlib
|
| 12 |
|
| 13 |
app = Flask(__name__)
|
| 14 |
+
app.secret_key = 'your_secret_key_here' # Замените на безопасный ключ
|
| 15 |
DATA_FILE = 'data_detobuv.json'
|
| 16 |
+
USERS_FILE = 'users.json'
|
| 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 |
+
STORE_ADDRESS = "Бишкек, Рынок Дордой, 9 ряд, 06 контейнер"
|
| 25 |
|
| 26 |
# Настройка логирования
|
| 27 |
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
| 58 |
logging.error(f"Ошибка при сохранении данных: {e}")
|
| 59 |
raise
|
| 60 |
|
| 61 |
+
def load_users():
|
| 62 |
+
try:
|
| 63 |
+
with open(USERS_FILE, 'r', encoding='utf-8') as file:
|
| 64 |
+
return json.load(file)
|
| 65 |
+
except FileNotFoundError:
|
| 66 |
+
return {}
|
| 67 |
+
except json.JSONDecodeError:
|
| 68 |
+
return {}
|
| 69 |
+
|
| 70 |
+
def save_users(users):
|
| 71 |
+
with open(USERS_FILE, 'w', encoding='utf-8') as file:
|
| 72 |
+
json.dump(users, file, ensure_ascii=False, indent=4)
|
| 73 |
+
|
| 74 |
+
def hash_password(password):
|
| 75 |
+
return hashlib.sha256(password.encode()).hexdigest()
|
| 76 |
+
|
| 77 |
def upload_db_to_hf():
|
| 78 |
try:
|
| 79 |
api = HfApi()
|
|
|
|
| 117 |
data = load_data()
|
| 118 |
products = data['products']
|
| 119 |
categories = data['categories']
|
| 120 |
+
is_authenticated = 'user' in session
|
| 121 |
|
| 122 |
catalog_html = '''
|
| 123 |
<!DOCTYPE html>
|
|
|
|
| 125 |
<head>
|
| 126 |
<meta charset="UTF-8">
|
| 127 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 128 |
+
<title>Детская обувь оптом, Дордой, ряд 9, контейнер 06</title>
|
| 129 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 130 |
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
| 131 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
|
|
|
| 158 |
padding: 15px 0;
|
| 159 |
border-bottom: 1px solid #e2e8f0;
|
| 160 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
.header h1 {
|
| 162 |
font-size: 1.5rem;
|
| 163 |
font-weight: 600;
|
| 164 |
+
}
|
| 165 |
+
.auth-links {
|
| 166 |
+
display: flex;
|
| 167 |
+
gap: 15px;
|
| 168 |
+
}
|
| 169 |
+
.auth-links a {
|
| 170 |
+
color: #3b82f6;
|
| 171 |
+
text-decoration: none;
|
| 172 |
+
font-weight: 500;
|
| 173 |
+
}
|
| 174 |
+
.auth-links a:hover {
|
| 175 |
+
text-decoration: underline;
|
| 176 |
}
|
| 177 |
.theme-toggle {
|
| 178 |
background: none;
|
|
|
|
| 426 |
background-color: #059669;
|
| 427 |
box-shadow: 0 4px 15px rgba(5, 150, 105, 0.4);
|
| 428 |
}
|
| 429 |
+
.store-address {
|
| 430 |
+
padding: 10px;
|
| 431 |
+
text-align: center;
|
| 432 |
+
font-style: italic;
|
| 433 |
+
color: #666;
|
| 434 |
+
}
|
| 435 |
</style>
|
| 436 |
</head>
|
| 437 |
<body>
|
| 438 |
<div class="container">
|
| 439 |
<div class="header">
|
|
|
|
| 440 |
<h1>Каталог</h1>
|
| 441 |
+
<div class="auth-links">
|
| 442 |
+
{% if is_authenticated %}
|
| 443 |
+
<span>Добро пожаловать, {{ session['user'] }}!</span>
|
| 444 |
+
<a href="{{ url_for('logout') }}">Выйти</a>
|
| 445 |
+
{% else %}
|
| 446 |
+
<a href="{{ url_for('login') }}">Войти</a>
|
| 447 |
+
<a href="{{ url_for('register') }}">Регистрация</a>
|
| 448 |
+
{% endif %}
|
| 449 |
+
</div>
|
| 450 |
<button class="theme-toggle" onclick="toggleTheme()">
|
| 451 |
<i class="fas fa-moon"></i>
|
| 452 |
</button>
|
|
|
|
| 474 |
</div>
|
| 475 |
{% endif %}
|
| 476 |
<h2>{{ product['name'] }}</h2>
|
| 477 |
+
{% if is_authenticated %}
|
| 478 |
<div class="product-price">{{ product['price'] }} с</div>
|
| 479 |
+
{% else %}
|
| 480 |
+
<div class="product-price">Цена доступна после входа</div>
|
| 481 |
+
{% endif %}
|
| 482 |
<p class="product-description">{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}</p>
|
| 483 |
<button class="product-button" onclick="openModal({{ loop.index0 }})">Подробнее</button>
|
| 484 |
+
{% if is_authenticated %}
|
| 485 |
<button class="product-button add-to-cart" onclick="openQuantityModal({{ loop.index0 }})">В корзину</button>
|
| 486 |
+
{% endif %}
|
| 487 |
</div>
|
| 488 |
{% endfor %}
|
| 489 |
</div>
|
| 490 |
+
<div class="store-address">{{ store_address }}</div>
|
| 491 |
</div>
|
| 492 |
|
| 493 |
<!-- Product Modal -->
|
|
|
|
| 714 |
</body>
|
| 715 |
</html>
|
| 716 |
'''
|
| 717 |
+
return render_template_string(catalog_html, products=products, categories=categories,
|
| 718 |
+
repo_id=REPO_ID, is_authenticated=is_authenticated,
|
| 719 |
+
store_address=STORE_ADDRESS)
|
| 720 |
|
| 721 |
@app.route('/product/<int:index>')
|
| 722 |
def product_detail(index):
|
| 723 |
data = load_data()
|
| 724 |
products = data['products']
|
| 725 |
+
is_authenticated = 'user' in session
|
| 726 |
try:
|
| 727 |
product = products[index]
|
| 728 |
except IndexError:
|
|
|
|
| 753 |
<div class="swiper-button-prev"></div>
|
| 754 |
</div>
|
| 755 |
<p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
|
| 756 |
+
{% if is_authenticated %}
|
| 757 |
<p><strong>Цена:</strong> {{ product['price'] }} с</p>
|
| 758 |
+
{% else %}
|
| 759 |
+
<p><strong>Цена:</strong> Доступна после входа</p>
|
| 760 |
+
{% endif %}
|
| 761 |
<p><strong>Описание:</strong> {{ product['description'] }}</p>
|
| 762 |
<p><strong>Доступные цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
|
| 763 |
</div>
|
| 764 |
'''
|
| 765 |
+
return render_template_string(detail_html, product=product, repo_id=REPO_ID, is_authenticated=is_authenticated)
|
| 766 |
+
|
| 767 |
+
@app.route('/register', methods=['GET', 'POST'])
|
| 768 |
+
def register():
|
| 769 |
+
if request.method == 'POST':
|
| 770 |
+
login = request.form.get('login')
|
| 771 |
+
password = request.form.get('password')
|
| 772 |
+
country = request.form.get('country')
|
| 773 |
+
city = request.form.get('city')
|
| 774 |
+
purchase_type = request.form.get('purchase_type')
|
| 775 |
+
|
| 776 |
+
if purchase_type == 'retail':
|
| 777 |
+
return render_template_string('''
|
| 778 |
+
<h2>Мы продаем только оптом</h2>
|
| 779 |
+
<p><a href="{{ url_for('register') }}">Назад к регистрации</a></p>
|
| 780 |
+
''')
|
| 781 |
+
|
| 782 |
+
users = load_users()
|
| 783 |
+
if login in users:
|
| 784 |
+
return "Пользователь с таким логином уже существует", 400
|
| 785 |
+
|
| 786 |
+
users[login] = {
|
| 787 |
+
'password': hash_password(password),
|
| 788 |
+
'country': country,
|
| 789 |
+
'city': city,
|
| 790 |
+
'purchase_type': purchase_type
|
| 791 |
+
}
|
| 792 |
+
save_users(users)
|
| 793 |
+
session['user'] = login
|
| 794 |
+
return redirect(url_for('catalog'))
|
| 795 |
+
|
| 796 |
+
return render_template_string('''
|
| 797 |
+
<!DOCTYPE html>
|
| 798 |
+
<html lang="ru">
|
| 799 |
+
<head>
|
| 800 |
+
<meta charset="UTF-8">
|
| 801 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 802 |
+
<title>Регистрация</title>
|
| 803 |
+
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
| 804 |
+
<style>
|
| 805 |
+
body {
|
| 806 |
+
font-family: 'Poppins', sans-serif;
|
| 807 |
+
background: linear-gradient(135deg, #f0f2f5, #e9ecef);
|
| 808 |
+
padding: 20px;
|
| 809 |
+
}
|
| 810 |
+
.container {
|
| 811 |
+
max-width: 500px;
|
| 812 |
+
margin: 0 auto;
|
| 813 |
+
background: #fff;
|
| 814 |
+
padding: 20px;
|
| 815 |
+
border-radius: 15px;
|
| 816 |
+
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
| 817 |
+
}
|
| 818 |
+
h2 {
|
| 819 |
+
text-align: center;
|
| 820 |
+
margin-bottom: 20px;
|
| 821 |
+
}
|
| 822 |
+
label {
|
| 823 |
+
display: block;
|
| 824 |
+
margin: 10px 0 5px;
|
| 825 |
+
}
|
| 826 |
+
input, select {
|
| 827 |
+
width: 100%;
|
| 828 |
+
padding: 10px;
|
| 829 |
+
margin-bottom: 15px;
|
| 830 |
+
border: 1px solid #e2e8f0;
|
| 831 |
+
border-radius: 8px;
|
| 832 |
+
}
|
| 833 |
+
button {
|
| 834 |
+
width: 100%;
|
| 835 |
+
padding: 10px;
|
| 836 |
+
background-color: #3b82f6;
|
| 837 |
+
color: white;
|
| 838 |
+
border: none;
|
| 839 |
+
border-radius: 8px;
|
| 840 |
+
cursor: pointer;
|
| 841 |
+
}
|
| 842 |
+
button:hover {
|
| 843 |
+
background-color: #2563eb;
|
| 844 |
+
}
|
| 845 |
+
</style>
|
| 846 |
+
</head>
|
| 847 |
+
<body>
|
| 848 |
+
<div class="container">
|
| 849 |
+
<h2>Регистрация</h2>
|
| 850 |
+
<form method="POST">
|
| 851 |
+
<label>Логин:</label>
|
| 852 |
+
<input type="text" name="login" required>
|
| 853 |
+
<label>Пароль:</label>
|
| 854 |
+
<input type="password" name="password" required>
|
| 855 |
+
<label>Страна:</label>
|
| 856 |
+
<input type="text" name="country" required>
|
| 857 |
+
<label>Город:</label>
|
| 858 |
+
<input type="text" name="city" required>
|
| 859 |
+
<label>Тип покупки:</label>
|
| 860 |
+
<select name="purchase_type" required>
|
| 861 |
+
<option value="wholesale">Оптом</option>
|
| 862 |
+
<option value="retail">В розницу</option>
|
| 863 |
+
</select>
|
| 864 |
+
<button type="submit">Зарегистрироваться</button>
|
| 865 |
+
</form>
|
| 866 |
+
<p style="text-align: center; margin-top: 15px;">
|
| 867 |
+
<a href="{{ url_for('login') }}">Уже есть аккаунт? Войти</a>
|
| 868 |
+
</p>
|
| 869 |
+
</div>
|
| 870 |
+
</body>
|
| 871 |
+
</html>
|
| 872 |
+
''')
|
| 873 |
+
|
| 874 |
+
@app.route('/login', methods=['GET', 'POST'])
|
| 875 |
+
def login():
|
| 876 |
+
if request.method == 'POST':
|
| 877 |
+
login = request.form.get('login')
|
| 878 |
+
password = request.form.get('password')
|
| 879 |
+
users = load_users()
|
| 880 |
+
|
| 881 |
+
if login in users and users[login]['password'] == hash_password(password):
|
| 882 |
+
session['user'] = login
|
| 883 |
+
return redirect(url_for('catalog'))
|
| 884 |
+
return "Неверный логин или пароль", 401
|
| 885 |
+
|
| 886 |
+
return render_template_string('''
|
| 887 |
+
<!DOCTYPE html>
|
| 888 |
+
<html lang="ru">
|
| 889 |
+
<head>
|
| 890 |
+
<meta charset="UTF-8">
|
| 891 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 892 |
+
<title>Вход</title>
|
| 893 |
+
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
| 894 |
+
<style>
|
| 895 |
+
body {
|
| 896 |
+
font-family: 'Poppins', sans-serif;
|
| 897 |
+
background: linear-gradient(135deg, #f0f2f5, #e9ecef);
|
| 898 |
+
padding: 20px;
|
| 899 |
+
}
|
| 900 |
+
.container {
|
| 901 |
+
max-width: 500px;
|
| 902 |
+
margin: 0 auto;
|
| 903 |
+
background: #fff;
|
| 904 |
+
padding: 20px;
|
| 905 |
+
border-radius: 15px;
|
| 906 |
+
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
| 907 |
+
}
|
| 908 |
+
h2 {
|
| 909 |
+
text-align: center;
|
| 910 |
+
margin-bottom: 20px;
|
| 911 |
+
}
|
| 912 |
+
label {
|
| 913 |
+
display: block;
|
| 914 |
+
margin: 10px 0 5px;
|
| 915 |
+
}
|
| 916 |
+
input {
|
| 917 |
+
width: 100%;
|
| 918 |
+
padding: 10px;
|
| 919 |
+
margin-bottom: 15px;
|
| 920 |
+
border: 1px solid #e2e8f0;
|
| 921 |
+
border-radius: 8px;
|
| 922 |
+
}
|
| 923 |
+
button {
|
| 924 |
+
width: 100%;
|
| 925 |
+
padding: 10px;
|
| 926 |
+
background-color: #3b82f6;
|
| 927 |
+
color: white;
|
| 928 |
+
border: none;
|
| 929 |
+
border-radius: 8px;
|
| 930 |
+
cursor: pointer;
|
| 931 |
+
}
|
| 932 |
+
button:hover {
|
| 933 |
+
background-color: #2563eb;
|
| 934 |
+
}
|
| 935 |
+
</style>
|
| 936 |
+
</head>
|
| 937 |
+
<body>
|
| 938 |
+
<div class="container">
|
| 939 |
+
<h2>Вход</h2>
|
| 940 |
+
<form method="POST">
|
| 941 |
+
<label>Логин:</label>
|
| 942 |
+
<input type="text" name="login" required>
|
| 943 |
+
<label>Пароль:</label>
|
| 944 |
+
<input type="password" name="password" required>
|
| 945 |
+
<button type="submit">Войти</button>
|
| 946 |
+
</form>
|
| 947 |
+
<p style="text-align: center; margin-top: 15px;">
|
| 948 |
+
<a href="{{ url_for('register') }}">Нет аккаунта? Зарегистрироваться</a>
|
| 949 |
+
</p>
|
| 950 |
+
</div>
|
| 951 |
+
</body>
|
| 952 |
+
</html>
|
| 953 |
+
''')
|
| 954 |
+
|
| 955 |
+
@app.route('/logout')
|
| 956 |
+
def logout():
|
| 957 |
+
session.pop('user', None)
|
| 958 |
+
return redirect(url_for('catalog'))
|
| 959 |
|
| 960 |
@app.route('/admin', methods=['GET', 'POST'])
|
| 961 |
def admin():
|
|
|
|
| 993 |
photos_list = []
|
| 994 |
|
| 995 |
if photos_files:
|
| 996 |
+
for photo in photos_files[:10]:
|
| 997 |
if photo and photo.filename:
|
| 998 |
photo_filename = secure_filename(photo.filename)
|
| 999 |
uploads_dir = 'uploads'
|
|
|
|
| 1040 |
|
| 1041 |
if photos_files and any(photo.filename for photo in photos_files):
|
| 1042 |
new_photos_list = []
|
| 1043 |
+
for photo in photos_files[:10]:
|
| 1044 |
if photo and photo.filename:
|
| 1045 |
photo_filename = secure_filename(photo.filename)
|
| 1046 |
uploads_dir = 'uploads'
|
|
|
|
| 1095 |
margin: 0 auto;
|
| 1096 |
}
|
| 1097 |
.header {
|
|
|
|
|
|
|
| 1098 |
padding: 15px 0;
|
| 1099 |
border-bottom: 1px solid #e2e8f0;
|
| 1100 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1101 |
h1, h2 {
|
| 1102 |
font-weight: 600;
|
| 1103 |
margin-bottom: 20px;
|
|
|
|
| 1183 |
<body>
|
| 1184 |
<div class="container">
|
| 1185 |
<div class="header">
|
|
|
|
| 1186 |
<h1>Админ-панель</h1>
|
| 1187 |
</div>
|
| 1188 |
<h1>Добавление товара</h1>
|