Eluza133 commited on
Commit
0aa5a15
·
verified ·
1 Parent(s): 63ba184

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +285 -19
app.py CHANGED
@@ -27,18 +27,20 @@ def load_data():
27
  data = json.load(file)
28
  if not isinstance(data, dict):
29
  logging.warning("Данные не в формате dict, инициализация пустой базы")
30
- return {'posts': [], 'users': {}, 'pending_organizations': []}
31
  if 'posts' not in data:
32
  data['posts'] = []
33
  if 'users' not in data:
34
  data['users'] = {}
35
  if 'pending_organizations' not in data:
36
  data['pending_organizations'] = []
 
 
37
  logging.info("Данные успешно загружены")
38
  return data
39
  except Exception as e:
40
  logging.error(f"Ошибка загрузки данных: {e}")
41
- return {'posts': [], 'users': {}, 'pending_organizations': []}
42
 
43
  def save_data(data):
44
  try:
@@ -80,7 +82,7 @@ def download_db_from_hf():
80
  logging.error(f"Ошибка скачивания базы: {e}")
81
  if not os.path.exists(DATA_FILE):
82
  with open(DATA_FILE, 'w', encoding='utf-8') as f:
83
- json.dump({'posts': [], 'users': {}, 'pending_organizations': []}, f)
84
 
85
  def periodic_backup():
86
  while True:
@@ -101,6 +103,7 @@ BASE_STYLE = '''
101
  --shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
102
  --glass-bg: rgba(255, 255, 255, 0.15);
103
  --transition: all 0.4s ease;
 
104
  }
105
  * { margin: 0; padding: 0; box-sizing: border-box; }
106
  body {
@@ -208,6 +211,12 @@ BASE_STYLE = '''
208
  transform: scale(1.08);
209
  background: #5439cc;
210
  }
 
 
 
 
 
 
211
  input, textarea, select {
212
  width: 100%;
213
  padding: 14px;
@@ -296,6 +305,9 @@ NAV_HTML = '''
296
  {% if is_authenticated %}
297
  <a href="{{ url_for('profile') }}" class="nav-link"><span>👤</span> Профиль ({{ username }})</a>
298
  <a href="{{ url_for('upload') }}" class="nav-link"><span>⬆️</span> Загрузить</a>
 
 
 
299
  <a href="{{ url_for('logout') }}" class="nav-link logout-btn"><span>🚪</span> Выйти</a>
300
  {% else %}
301
  <a href="{{ url_for('login') }}" class="nav-link"><span>🔑</span> Войти</a>
@@ -622,6 +634,8 @@ def feed():
622
  posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
623
  is_authenticated = 'username' in session
624
  username = session.get('username', None)
 
 
625
 
626
  search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' else request.args.get('search', '').strip().lower()
627
  if search_query:
@@ -745,16 +759,21 @@ def feed():
745
  </div>
746
  <div class="post-grid">
747
  {% for post in posts %}
748
- <a href="{{ url_for('post_page', post_id=post['id']) }}" class="post-item">
749
- <video class="post-preview" preload="metadata" muted>
750
- <source src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/videos/{{ post['filename'] }}" type="video/mp4">
751
- </video>
752
- <h2>{{ post['title'] }}</h2>
 
 
753
  <p>{{ post['description'] }}</p>
754
  <p class="price">Цена: {{ post['price'] }} {{ post['currency'] }}</p>
755
  <p>Загрузил: <a href="{{ url_for('user_profile', username=post['uploader']) }}" class="username-link">{{ post['uploader'] }}</a> | {{ post['upload_date'] }}</p>
756
  <p class="stats">Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}</p>
757
- </a>
 
 
 
758
  {% endfor %}
759
  </div>
760
  </div>
@@ -781,6 +800,16 @@ def feed():
781
  document.getElementById('imageModal').style.display = 'none';
782
  }
783
  }
 
 
 
 
 
 
 
 
 
 
784
  window.onload = () => {
785
  if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
786
  const videos = document.querySelectorAll('.post-preview');
@@ -794,7 +823,7 @@ def feed():
794
  </body>
795
  </html>
796
  '''
797
- return render_template_string(html, posts=posts, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query)
798
 
799
  # Страница публикации
800
  @app.route('/post/<post_id>', methods=['GET', 'POST'])
@@ -806,6 +835,8 @@ def post_page(post_id):
806
 
807
  is_authenticated = 'username' in session
808
  username = session.get('username', None)
 
 
809
  post['views'] = post.get('views', 0) + 1
810
  save_data(data)
811
 
@@ -912,6 +943,7 @@ def post_page(post_id):
912
  {% if username in post['likes'] %}Убрать лайк{% else %}Лайк{% endif %}
913
  </button>
914
  </form>
 
915
  <form method="POST" class="comment-section">
916
  <textarea name="comment" placeholder="Оставьте комментарий" rows="4"></textarea>
917
  <button type="submit" class="btn">Отправить</button>
@@ -936,6 +968,16 @@ def post_page(post_id):
936
  document.body.classList.toggle('dark');
937
  localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
938
  }
 
 
 
 
 
 
 
 
 
 
939
  window.onload = () => {
940
  if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
941
  };
@@ -943,7 +985,7 @@ def post_page(post_id):
943
  </body>
944
  </html>
945
  '''
946
- return render_template_string(html, post=post, repo_id=REPO_ID, is_authenticated=is_authenticated, username=username)
947
 
948
  # Профиль пользователя
949
  @app.route('/profile', methods=['GET', 'POST'])
@@ -999,6 +1041,27 @@ def profile():
999
  data['users'][username]['link'] = link
1000
  save_data(data)
1001
  return redirect(url_for('profile'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1002
 
1003
  html = '''
1004
  <!DOCTYPE html>
@@ -1046,12 +1109,12 @@ def profile():
1046
  font-size: 1.2em;
1047
  margin-bottom: 15px;
1048
  }
1049
- .post-grid {
1050
  display: grid;
1051
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
1052
  gap: 30px;
1053
  }
1054
- .post-item {
1055
  background: var(--card-bg-light);
1056
  backdrop-filter: blur(20px);
1057
  padding: 25px;
@@ -1059,10 +1122,10 @@ def profile():
1059
  box-shadow: var(--shadow);
1060
  transition: var(--transition);
1061
  }
1062
- body.dark .post-item {
1063
  background: var(--card-bg-dark);
1064
  }
1065
- .post-item:hover {
1066
  transform: translateY(-15px);
1067
  }
1068
  .post-preview {
@@ -1072,7 +1135,7 @@ def profile():
1072
  border-radius: 15px;
1073
  margin-bottom: 20px;
1074
  }
1075
- .post-item h3 {
1076
  font-size: 1.5em;
1077
  margin-bottom: 15px;
1078
  }
@@ -1099,6 +1162,12 @@ def profile():
1099
  font-weight: 600;
1100
  color: var(--primary);
1101
  }
 
 
 
 
 
 
1102
  </style>
1103
  </head>
1104
  <body>
@@ -1131,6 +1200,9 @@ def profile():
1131
  {% if user_type == 'seller' and verified %}
1132
  <a href="{{ url_for('upload') }}" class="btn">Добавить видео</a>
1133
  {% endif %}
 
 
 
1134
  <h2>Ваши видео</h2>
1135
  <div class="post-grid">
1136
  {% if user_posts %}
@@ -1169,8 +1241,54 @@ def profile():
1169
  alert('Ссылка скопирована!');
1170
  });
1171
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1172
  window.onload = () => {
1173
  if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
 
1174
  const videos = document.querySelectorAll('.post-preview');
1175
  videos.forEach(video => {
1176
  video.addEventListener('loadedmetadata', () => {
@@ -1193,6 +1311,7 @@ def user_profile(username):
1193
 
1194
  user_posts = sorted([p for p in data['posts'] if p['uploader'] == username], key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
1195
  is_authenticated = 'username' in session
 
1196
  total_views = sum(post.get('views', 0) for post in user_posts)
1197
  total_likes = sum(len(post.get('likes', [])) for post in user_posts)
1198
  user_data = data['users'].get(username, {})
@@ -1437,6 +1556,8 @@ def upload():
1437
 
1438
  is_authenticated = 'username' in session
1439
  username = session.get('username', None)
 
 
1440
  html = '''
1441
  <!DOCTYPE html>
1442
  <html lang="ru">
@@ -1566,7 +1687,150 @@ def upload():
1566
  </body>
1567
  </html>
1568
  '''
1569
- return render_template_string(html, username=username, is_authenticated=is_authenticated)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1570
 
1571
  # Админ-панель
1572
  @app.route('/admhosto', methods=['GET', 'POST'])
@@ -1576,6 +1840,8 @@ def admin_panel():
1576
  pending_orgs = data.get('pending_organizations', [])
1577
  is_authenticated = 'username' in session
1578
  username = session.get('username', None)
 
 
1579
 
1580
  if request.method == 'POST':
1581
  if 'delete' in request.form:
@@ -1603,7 +1869,7 @@ def admin_panel():
1603
  posts = [post for post in posts if search_query in post['title'].lower() or search_query in post['description'].lower()]
1604
 
1605
  html = '''
1606
- <!DOCTYPE html>
1607
  <html lang="ru">
1608
  <head>
1609
  <meta charset="UTF-8">
@@ -1797,7 +2063,7 @@ def admin_panel():
1797
  </body>
1798
  </html>
1799
  '''
1800
- return render_template_string(html, posts=posts, pending_orgs=pending_orgs, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query)
1801
 
1802
  if __name__ == '__main__':
1803
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
 
27
  data = json.load(file)
28
  if not isinstance(data, dict):
29
  logging.warning("Данные не в формате dict, инициализация пустой базы")
30
+ return {'posts': [], 'users': {}, 'pending_organizations': [], 'orders': {}}
31
  if 'posts' not in data:
32
  data['posts'] = []
33
  if 'users' not in data:
34
  data['users'] = {}
35
  if 'pending_organizations' not in data:
36
  data['pending_organizations'] = []
37
+ if 'orders' not in data:
38
+ data['orders'] = {}
39
  logging.info("Данные успешно загружены")
40
  return data
41
  except Exception as e:
42
  logging.error(f"Ошибка загрузки данных: {e}")
43
+ return {'posts': [], 'users': {}, 'pending_organizations': [], 'orders': {}}
44
 
45
  def save_data(data):
46
  try:
 
82
  logging.error(f"Ошибка скачивания базы: {e}")
83
  if not os.path.exists(DATA_FILE):
84
  with open(DATA_FILE, 'w', encoding='utf-8') as f:
85
+ json.dump({'posts': [], 'users': {}, 'pending_organizations': [], 'orders': {}}, f)
86
 
87
  def periodic_backup():
88
  while True:
 
103
  --shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
104
  --glass-bg: rgba(255, 255, 255, 0.15);
105
  --transition: all 0.4s ease;
106
+ --cart-btn: #10b981;
107
  }
108
  * { margin: 0; padding: 0; box-sizing: border-box; }
109
  body {
 
211
  transform: scale(1.08);
212
  background: #5439cc;
213
  }
214
+ .cart-btn {
215
+ background: var(--cart-btn);
216
+ }
217
+ .cart-btn:hover {
218
+ background: #0d9f6e;
219
+ }
220
  input, textarea, select {
221
  width: 100%;
222
  padding: 14px;
 
305
  {% if is_authenticated %}
306
  <a href="{{ url_for('profile') }}" class="nav-link"><span>👤</span> Профиль ({{ username }})</a>
307
  <a href="{{ url_for('upload') }}" class="nav-link"><span>⬆️</span> Загрузить</a>
308
+ {% if user_type == 'seller' and verified %}
309
+ <a href="{{ url_for('orders') }}" class="nav-link"><span>🛒</span> Заказы</a>
310
+ {% endif %}
311
  <a href="{{ url_for('logout') }}" class="nav-link logout-btn"><span>🚪</span> Выйти</a>
312
  {% else %}
313
  <a href="{{ url_for('login') }}" class="nav-link"><span>🔑</span> Войти</a>
 
634
  posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
635
  is_authenticated = 'username' in session
636
  username = session.get('username', None)
637
+ user_type = data['users'].get(username, {}).get('type') if username else None
638
+ verified = data['users'].get(username, {}).get('verified', False) if username else False
639
 
640
  search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' else request.args.get('search', '').strip().lower()
641
  if search_query:
 
759
  </div>
760
  <div class="post-grid">
761
  {% for post in posts %}
762
+ <div class="post-item">
763
+ <a href="{{ url_for('post_page', post_id=post['id']) }}">
764
+ <video class="post-preview" preload="metadata" muted>
765
+ <source src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/videos/{{ post['filename'] }}" type="video/mp4">
766
+ </video>
767
+ <h2>{{ post['title'] }}</h2>
768
+ </a>
769
  <p>{{ post['description'] }}</p>
770
  <p class="price">Цена: {{ post['price'] }} {{ post['currency'] }}</p>
771
  <p>Загрузил: <a href="{{ url_for('user_profile', username=post['uploader']) }}" class="username-link">{{ post['uploader'] }}</a> | {{ post['upload_date'] }}</p>
772
  <p class="stats">Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}</p>
773
+ {% if is_authenticated %}
774
+ <button class="btn cart-btn" onclick="addToCart('{{ post['id'] }}', '{{ post['title'] }}', '{{ post['price'] }}', '{{ post['currency'] }}', '{{ post['uploader'] }}')">В корзину</button>
775
+ {% endif %}
776
+ </div>
777
  {% endfor %}
778
  </div>
779
  </div>
 
800
  document.getElementById('imageModal').style.display = 'none';
801
  }
802
  }
803
+ function addToCart(postId, title, price, currency, uploader) {
804
+ let cart = JSON.parse(localStorage.getItem('cart') || '[]');
805
+ if (!cart.some(item => item.postId === postId)) {
806
+ cart.push({ postId, title, price, currency, uploader });
807
+ localStorage.setItem('cart', JSON.stringify(cart));
808
+ alert('Добавлено в корзину!');
809
+ } else {
810
+ alert('Этот товар уже в корзине!');
811
+ }
812
+ }
813
  window.onload = () => {
814
  if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
815
  const videos = document.querySelectorAll('.post-preview');
 
823
  </body>
824
  </html>
825
  '''
826
+ return render_template_string(html, posts=posts, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query, user_type=user_type, verified=verified)
827
 
828
  # Страница публикации
829
  @app.route('/post/<post_id>', methods=['GET', 'POST'])
 
835
 
836
  is_authenticated = 'username' in session
837
  username = session.get('username', None)
838
+ user_type = data['users'].get(username, {}).get('type') if username else None
839
+ verified = data['users'].get(username, {}).get('verified', False) if username else False
840
  post['views'] = post.get('views', 0) + 1
841
  save_data(data)
842
 
 
943
  {% if username in post['likes'] %}Убрать лайк{% else %}Лайк{% endif %}
944
  </button>
945
  </form>
946
+ <button class="btn cart-btn" onclick="addToCart('{{ post['id'] }}', '{{ post['title'] }}', '{{ post['price'] }}', '{{ post['currency'] }}', '{{ post['uploader'] }}')">В корзину</button>
947
  <form method="POST" class="comment-section">
948
  <textarea name="comment" placeholder="Оставьте комментарий" rows="4"></textarea>
949
  <button type="submit" class="btn">Отправить</button>
 
968
  document.body.classList.toggle('dark');
969
  localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
970
  }
971
+ function addToCart(postId, title, price, currency, uploader) {
972
+ let cart = JSON.parse(localStorage.getItem('cart') || '[]');
973
+ if (!cart.some(item => item.postId === postId)) {
974
+ cart.push({ postId, title, price, currency, uploader });
975
+ localStorage.setItem('cart', JSON.stringify(cart));
976
+ alert('Добавлено в корзину!');
977
+ } else {
978
+ alert('Этот товар уже в корзине!');
979
+ }
980
+ }
981
  window.onload = () => {
982
  if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
983
  };
 
985
  </body>
986
  </html>
987
  '''
988
+ return render_template_string(html, post=post, repo_id=REPO_ID, is_authenticated=is_authenticated, username=username, user_type=user_type, verified=verified)
989
 
990
  # Профиль пользователя
991
  @app.route('/profile', methods=['GET', 'POST'])
 
1041
  data['users'][username]['link'] = link
1042
  save_data(data)
1043
  return redirect(url_for('profile'))
1044
+ elif 'checkout' in request.form:
1045
+ cart = json.loads(request.form.get('cart_data', '[]'))
1046
+ if cart:
1047
+ for item in cart:
1048
+ seller = item['uploader']
1049
+ if seller not in data['orders']:
1050
+ data['orders'][seller] = []
1051
+ order_id = f"order_{len(data['orders'].get(seller, [])) + 1}_{int(time.time())}"
1052
+ data['orders'][seller].append({
1053
+ 'order_id': order_id,
1054
+ 'buyer': username,
1055
+ 'post_id': item['postId'],
1056
+ 'title': item['title'],
1057
+ 'price': item['price'],
1058
+ 'currency': item['currency'],
1059
+ 'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
1060
+ 'status': 'pending'
1061
+ })
1062
+ save_data(data)
1063
+ flash('Заказ успешно оформлен!', 'success')
1064
+ return redirect(url_for('profile'))
1065
 
1066
  html = '''
1067
  <!DOCTYPE html>
 
1109
  font-size: 1.2em;
1110
  margin-bottom: 15px;
1111
  }
1112
+ .post-grid, .cart-grid {
1113
  display: grid;
1114
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
1115
  gap: 30px;
1116
  }
1117
+ .post-item, .cart-item {
1118
  background: var(--card-bg-light);
1119
  backdrop-filter: blur(20px);
1120
  padding: 25px;
 
1122
  box-shadow: var(--shadow);
1123
  transition: var(--transition);
1124
  }
1125
+ body.dark .post-item, body.dark .cart-item {
1126
  background: var(--card-bg-dark);
1127
  }
1128
+ .post-item:hover, .cart-item:hover {
1129
  transform: translateY(-15px);
1130
  }
1131
  .post-preview {
 
1135
  border-radius: 15px;
1136
  margin-bottom: 20px;
1137
  }
1138
+ .post-item h3, .cart-item h3 {
1139
  font-size: 1.5em;
1140
  margin-bottom: 15px;
1141
  }
 
1162
  font-weight: 600;
1163
  color: var(--primary);
1164
  }
1165
+ .remove-cart-btn {
1166
+ background: #ef4444;
1167
+ }
1168
+ .remove-cart-btn:hover {
1169
+ background: #dc2626;
1170
+ }
1171
  </style>
1172
  </head>
1173
  <body>
 
1200
  {% if user_type == 'seller' and verified %}
1201
  <a href="{{ url_for('upload') }}" class="btn">Добавить видео</a>
1202
  {% endif %}
1203
+ <h2>Корзина</h2>
1204
+ <div class="cart-grid" id="cartGrid"></div>
1205
+ <button class="btn" onclick="checkout()" style="margin-top: 20px;">Оформить заказ</button>
1206
  <h2>Ваши видео</h2>
1207
  <div class="post-grid">
1208
  {% if user_posts %}
 
1241
  alert('Ссылка скопирована!');
1242
  });
1243
  }
1244
+ function renderCart() {
1245
+ const cart = JSON.parse(localStorage.getItem('cart') || '[]');
1246
+ const cartGrid = document.getElementById('cartGrid');
1247
+ cartGrid.innerHTML = '';
1248
+ cart.forEach(item => {
1249
+ const div = document.createElement('div');
1250
+ div.className = 'cart-item';
1251
+ div.innerHTML = `
1252
+ <h3>${item.title}</h3>
1253
+ <p class="price">Цена: ${item.price} ${item.currency}</p>
1254
+ <p>Продавец: <a href="/profile/${item.uploader}" class="username-link">${item.uploader}</a></p>
1255
+ <button class="btn remove-cart-btn" onclick="removeFromCart('${item.postId}')">Удалить</button>
1256
+ `;
1257
+ cartGrid.appendChild(div);
1258
+ });
1259
+ }
1260
+ function removeFromCart(postId) {
1261
+ let cart = JSON.parse(localStorage.getItem('cart') || '[]');
1262
+ cart = cart.filter(item => item.postId !== postId);
1263
+ localStorage.setItem('cart', JSON.stringify(cart));
1264
+ renderCart();
1265
+ }
1266
+ function checkout() {
1267
+ const cart = JSON.parse(localStorage.getItem('cart') || '[]');
1268
+ if (cart.length === 0) {
1269
+ alert('Корзина пуста!');
1270
+ return;
1271
+ }
1272
+ const form = document.createElement('form');
1273
+ form.method = 'POST';
1274
+ form.action = '/profile';
1275
+ const input = document.createElement('input');
1276
+ input.type = 'hidden';
1277
+ input.name = 'checkout';
1278
+ input.value = 'true';
1279
+ const cartData = document.createElement('input');
1280
+ cartData.type = 'hidden';
1281
+ cartData.name = 'cart_data';
1282
+ cartData.value = JSON.stringify(cart);
1283
+ form.appendChild(input);
1284
+ form.appendChild(cartData);
1285
+ document.body.appendChild(form);
1286
+ form.submit();
1287
+ localStorage.removeItem('cart');
1288
+ }
1289
  window.onload = () => {
1290
  if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
1291
+ renderCart();
1292
  const videos = document.querySelectorAll('.post-preview');
1293
  videos.forEach(video => {
1294
  video.addEventListener('loadedmetadata', () => {
 
1311
 
1312
  user_posts = sorted([p for p in data['posts'] if p['uploader'] == username], key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
1313
  is_authenticated = 'username' in session
1314
+ current_user = session.get('username')
1315
  total_views = sum(post.get('views', 0) for post in user_posts)
1316
  total_likes = sum(len(post.get('likes', [])) for post in user_posts)
1317
  user_data = data['users'].get(username, {})
 
1556
 
1557
  is_authenticated = 'username' in session
1558
  username = session.get('username', None)
1559
+ user_type = data['users'].get(username, {}).get('type')
1560
+ verified = data['users'].get(username, {}).get('verified', False)
1561
  html = '''
1562
  <!DOCTYPE html>
1563
  <html lang="ru">
 
1687
  </body>
1688
  </html>
1689
  '''
1690
+ return render_template_string(html, username=username, is_authenticated=is_authenticated, user_type=user_type, verified=verified)
1691
+
1692
+ # Заказы продавца
1693
+ @app.route('/orders', methods=['GET', 'POST'])
1694
+ def orders():
1695
+ if 'username' not in session:
1696
+ flash('Войдите, чтобы просмотреть заказы!', 'error')
1697
+ return redirect(url_for('login'))
1698
+
1699
+ data = load_data()
1700
+ username = session['username']
1701
+ user_data = data['users'].get(username, {})
1702
+ if user_data.get('type') != 'seller' or not user_data.get('verified'):
1703
+ flash('Только проверенные продавцы могут просматривать заказы!', 'error')
1704
+ return redirect(url_for('profile'))
1705
+
1706
+ orders = data['orders'].get(username, [])
1707
+ is_authenticated = 'username' in session
1708
+ user_type = user_data.get('type')
1709
+ verified = user_data.get('verified', False)
1710
+
1711
+ if request.method == 'POST':
1712
+ if 'update_status' in request.form:
1713
+ order_id = request.form.get('order_id')
1714
+ new_status = request.form.get('status')
1715
+ for order in orders:
1716
+ if order['order_id'] == order_id:
1717
+ order['status'] = new_status
1718
+ break
1719
+ save_data(data)
1720
+ return redirect(url_for('orders'))
1721
+
1722
+ html = '''
1723
+ <!DOCTYPE html>
1724
+ <html lang="ru">
1725
+ <head>
1726
+ <meta charset="UTF-8">
1727
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1728
+ <title>Заказы - {{ username }}</title>
1729
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&display=swap" rel="stylesheet">
1730
+ <style>
1731
+ ''' + BASE_STYLE + '''
1732
+ .container {
1733
+ max-width: 950px;
1734
+ }
1735
+ h1 {
1736
+ font-size: 2.5em;
1737
+ font-weight: 700;
1738
+ margin-bottom: 40px;
1739
+ background: linear-gradient(45deg, var(--primary), var(--secondary));
1740
+ -webkit-background-clip: text;
1741
+ color: transparent;
1742
+ }
1743
+ .order-grid {
1744
+ display: grid;
1745
+ grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
1746
+ gap: 30px;
1747
+ }
1748
+ .order-item {
1749
+ background: var(--card-bg-light);
1750
+ backdrop-filter: blur(20px);
1751
+ padding: 25px;
1752
+ border-radius: 20px;
1753
+ box-shadow: var(--shadow);
1754
+ transition: var(--transition);
1755
+ }
1756
+ body.dark .order-item {
1757
+ background: var(--card-bg-dark);
1758
+ }
1759
+ .order-item:hover {
1760
+ transform: translateY(-15px);
1761
+ }
1762
+ .order-item h3 {
1763
+ font-size: 1.5em;
1764
+ margin-bottom: 15px;
1765
+ }
1766
+ .order-item p {
1767
+ margin-bottom: 15px;
1768
+ font-size: 1.1em;
1769
+ }
1770
+ .price {
1771
+ font-weight: 600;
1772
+ color: var(--primary);
1773
+ }
1774
+ .username-link {
1775
+ color: var(--primary);
1776
+ font-weight: 600;
1777
+ text-decoration: none;
1778
+ }
1779
+ .username-link:hover {
1780
+ text-decoration: underline;
1781
+ }
1782
+ select {
1783
+ width: auto;
1784
+ display: inline-block;
1785
+ }
1786
+ </style>
1787
+ </head>
1788
+ <body>
1789
+ <button class="menu-btn" onclick="toggleSidebar()">☰</button>
1790
+ ''' + NAV_HTML + '''
1791
+ <button class="theme-toggle" onclick="toggleTheme()">🌙</button>
1792
+ <div class="container">
1793
+ <h1>Ваши заказы</h1>
1794
+ <div class="order-grid">
1795
+ {% if orders %}
1796
+ {% for order in orders %}
1797
+ <div class="order-item">
1798
+ <h3>{{ order['title'] }}</h3>
1799
+ <p class="price">Цена: {{ order['price'] }} {{ order['currency'] }}</p>
1800
+ <p>Покупатель: <a href="{{ url_for('user_profile', username=order['buyer']) }}" class="username-link">{{ order['buyer'] }}</a></p>
1801
+ <p>Дата: {{ order['date'] }}</p>
1802
+ <form method="POST">
1803
+ <input type="hidden" name="order_id" value="{{ order['order_id'] }}">
1804
+ <select name="status" onchange="this.form.submit()">
1805
+ <option value="pending" {% if order['status'] == 'pending' %}selected{% endif %}>В ожидании</option>
1806
+ <option value="processing" {% if order['status'] == 'processing' %}selected{% endif %}>В обработке</option>
1807
+ <option value="completed" {% if order['status'] == 'completed' %}selected{% endif %}>Завершено</option>
1808
+ </select>
1809
+ <input type="hidden" name="update_status" value="true">
1810
+ </form>
1811
+ </div>
1812
+ {% endfor %}
1813
+ {% else %}
1814
+ <p style="font-size: 1.2em;">У вас пока нет заказов.</p>
1815
+ {% endif %}
1816
+ </div>
1817
+ </div>
1818
+ <script>
1819
+ function toggleSidebar() {
1820
+ document.getElementById('sidebar').classList.toggle('active');
1821
+ }
1822
+ function toggleTheme() {
1823
+ document.body.classList.toggle('dark');
1824
+ localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
1825
+ }
1826
+ window.onload = () => {
1827
+ if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
1828
+ };
1829
+ </script>
1830
+ </body>
1831
+ </html>
1832
+ '''
1833
+ return render_template_string(html, username=username, orders=orders, is_authenticated=is_authenticated, user_type=user_type, verified=verified)
1834
 
1835
  # Админ-панель
1836
  @app.route('/admhosto', methods=['GET', 'POST'])
 
1840
  pending_orgs = data.get('pending_organizations', [])
1841
  is_authenticated = 'username' in session
1842
  username = session.get('username', None)
1843
+ user_type = data['users'].get(username, {}).get('type') if username else None
1844
+ verified = data['users'].get(username, {}).get('verified', False) if username else False
1845
 
1846
  if request.method == 'POST':
1847
  if 'delete' in request.form:
 
1869
  posts = [post for post in posts if search_query in post['title'].lower() or search_query in post['description'].lower()]
1870
 
1871
  html = '''
1872
+ <!DOCTYPE html
1873
  <html lang="ru">
1874
  <head>
1875
  <meta charset="UTF-8">
 
2063
  </body>
2064
  </html>
2065
  '''
2066
+ return render_template_string(html, posts=posts, pending_orgs=pending_orgs, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query, user_type=user_type, verified=verified)
2067
 
2068
  if __name__ == '__main__':
2069
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)