Kgshop commited on
Commit
4291477
·
verified ·
1 Parent(s): c688616

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +262 -38
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
- LOGO_URL = "https://cdn-avatars.huggingface.co/v1/production/uploads/67b22aaeae9b6a59f1cfb849/NQvBksXzJItYt6hfFjyaB.jpeg"
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>Детская обувь оптом , Дордой , ряд 9 , контейнер 06 </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
- margin-left: 15px;
 
 
 
 
 
 
 
 
 
 
 
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, repo_id=REPO_ID)
 
 
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]: # Ограничение до 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]: # Огр��ничение до 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>