diff --git "a/app.py" "b/app.py"
--- "a/app.py"
+++ "b/app.py"
@@ -1,1112 +1,2087 @@
-from flask import Flask, render_template_string, request, redirect, url_for
+from flask import Flask, render_template_string, request, redirect, url_for, session
+import random
+import string
import json
import os
-import logging
-import threading
+from flask_socketio import SocketIO, join_room, leave_room, emit
+import hashlib
import time
+import threading
from datetime import datetime
from huggingface_hub import HfApi, hf_hub_download
from huggingface_hub.utils import RepositoryNotFoundError
-from werkzeug.utils import secure_filename
app = Flask(__name__)
-DATA_FILE = 'data_luxoff.json'
+app.config['SECRET_KEY'] = 'your-very-secret-key-here'
+socketio = SocketIO(app)
-# Настройки Hugging Face
-REPO_ID = "flpolprojects/Clients"
-HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
-HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
+# --- Hugging Face Hub Settings ---
+REPO_ID = "flpolprojects/Clients" # ваш репозиторий
+HF_TOKEN_WRITE = os.getenv("HF_TOKEN") # токен с правами на запись
+HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") # токен с правами на чтение (можно тот же, если у него есть и чтение, и запись)
-# Ссылка на логотип
-LOGO_URL = "https://cdn-avatars.huggingface.co/v1/production/uploads/63b6a710ccebeadccc864577/j1KeCVskZ3ZxI7oYQuu7o.png"
+# --- File Paths ---
+ROOMS_DB = os.path.join(app.root_path, 'rooms.json')
+USERS_DB = os.path.join(app.root_path, 'users.json')
+GAMES_DB = os.path.join(app.root_path, 'games.json')
-# Настройка логирования
-logging.basicConfig(level=logging.DEBUG)
+# --- Data Loading and Saving with Hugging Face Backup ---
-def load_data():
+def load_json(file_path, default={}):
try:
- download_db_from_hf()
- with open(DATA_FILE, 'r', encoding='utf-8') as file:
- data = json.load(file)
- logging.info("Данные успешно загружены из JSON")
- if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
- return {'products': [], 'categories': [] if not isinstance(data, list) else data}
- return data
- except FileNotFoundError:
- logging.warning("Локальный файл базы данных не найден после скачивания.")
- return {'products': [], 'categories': []}
- except json.JSONDecodeError:
- logging.error("Ошибка: Невозможно декодировать JSON файл.")
- return {'products': [], 'categories': []}
- except RepositoryNotFoundError:
- logging.error("Репозиторий не найден. Создание локальной базы данных.")
- return {'products': [], 'categories': []}
+ download_db_from_hf() # Скачиваем перед загрузкой
+ if os.path.exists(file_path):
+ with open(file_path, 'r', encoding='utf-8') as f:
+ return json.load(f)
+ return default
+ except (FileNotFoundError, json.JSONDecodeError) as e:
+ print(f"Ошибка загрузки JSON из {file_path}: {e}")
+ return default
except Exception as e:
- logging.error(f"Произошла ошибка при загрузке данных: {e}")
- return {'products': [], 'categories': []}
+ print(f"Непредвиденная ошибка при загрузке: {e}") #Добавил обработку
+ return default
-def save_data(data):
+def save_json(file_path, data):
try:
- with open(DATA_FILE, 'w', encoding='utf-8') as file:
- json.dump(data, file, ensure_ascii=False, indent=4)
- logging.info("Данные успешно сохранены в JSON")
- upload_db_to_hf()
- except Exception as e:
- logging.error(f"Ошибка при сохранении данных: {e}")
- raise
+ with open(file_path, 'w', encoding='utf-8') as f:
+ json.dump(data, f, indent=4, ensure_ascii=False)
+ upload_db_to_hf() # Загружаем после сохранения
+ except OSError as e:
+ print(f"Ошибка сохранения JSON в {file_path}: {e}")
+ except Exception as e: #Добавил обработку
+ print(f"Непредвиденная ошибка при сохранении: {e}")
def upload_db_to_hf():
try:
api = HfApi()
- api.upload_file(
- path_or_fileobj=DATA_FILE,
- path_in_repo=DATA_FILE,
- repo_id=REPO_ID,
- repo_type="dataset",
- token=HF_TOKEN_WRITE,
- commit_message=f"Автоматическое резервное копирование базы данных {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
- )
- logging.info("Резервная копия JSON базы успешно загружена на Hugging Face.")
+ # Загружаем все три JSON файла
+ for file_path, repo_path in [(ROOMS_DB, "rooms.json"), (USERS_DB, "users.json"), (GAMES_DB, "games.json")]:
+ if os.path.exists(file_path): # Проверяем наличие файла
+ api.upload_file(
+ path_or_fileobj=file_path,
+ path_in_repo=repo_path,
+ repo_id=REPO_ID,
+ repo_type="dataset",
+ token=HF_TOKEN_WRITE,
+ commit_message=f"Backup: {repo_path} ({datetime.now().strftime('%Y-%m-%d %H:%M:%S')})"
+ )
+ print(f"Файл {file_path} успешно загружен на Hugging Face Hub.")
+ else:
+ print(f"Файл {file_path} не существует, пропуск загрузки.")
except Exception as e:
- logging.error(f"Ошибка при загрузке резервной копии: {e}")
+ print(f"Ошибка при загрузке файлов на Hugging Face Hub: {e}")
def download_db_from_hf():
try:
- hf_hub_download(
- repo_id=REPO_ID,
- filename=DATA_FILE,
- repo_type="dataset",
- token=HF_TOKEN_READ,
- local_dir=".",
- local_dir_use_symlinks=False
- )
- logging.info("JSON база успешно скачана из Hugging Face.")
- except RepositoryNotFoundError as e:
- logging.error(f"Репозиторий не найден: {e}")
- raise
+ api = HfApi()
+ # Скачиваем все три JSON файла
+ for file_path, repo_path in [(ROOMS_DB, "rooms.json"), (USERS_DB, "users.json"), (GAMES_DB, "games.json")]:
+ try:
+ hf_hub_download(
+ repo_id=REPO_ID,
+ filename=repo_path,
+ repo_type="dataset",
+ token=HF_TOKEN_READ,
+ local_dir=".", # Сохраняем в текущую директорию
+ local_dir_use_symlinks=False # Важно для корректной работы на некоторых платформах
+ )
+ print(f"Файл {repo_path} успешно скачан с Hugging Face Hub.")
+ except RepositoryNotFoundError:
+ print(f"Файл {repo_path} не найден в репозитории. Создание локального.")
+ # Создаем пустой файл, если его нет в репозитории
+ if not os.path.exists(file_path):
+ with open(file_path, 'w') as f:
+ json.dump({}, f) # Создание пустого
+ except Exception as e:
+ print(f"Ошибка при скачивании файла {repo_path}: {e}")
+
+
except Exception as e:
- logging.error(f"Ошибка при скачивании JSON базы: {e}")
- raise
+ print(f"Ошибка при скачивании файлов с Hugging Face Hub: {e}")
def periodic_backup():
while True:
upload_db_to_hf()
- time.sleep(15)
+ time.sleep(15) # Резервное копирование каждые 15 секунд
-@app.route('/')
-def catalog():
- data = load_data()
- products = data['products']
- categories = data['categories']
-
- catalog_html = '''
-
-
-
-
-
- LUXoff женская одежда оптом
-
-
-
-
+
+
+
+
+
Видеоконференция
+
+
+
+''')
+
+
+@app.route('/dashboard', methods=['GET', 'POST'])
+def dashboard():
+ if 'username' not in session:
+ return redirect(url_for('index'))
+
+ if request.method == 'POST':
+ action = request.form.get('action')
+ if action == 'create':
+ token = generate_token()
+ rooms[token] = {'users': [], 'max_users': 5, 'admin': session['username'], 'current_game': None}
+ save_json(ROOMS_DB, rooms)
+ return redirect(url_for('room', token=token))
+ elif action == 'join':
+ token = request.form.get('token')
+ if token in rooms and len(rooms[token]['users']) < rooms[token]['max_users']:
+ return redirect(url_for('room', token=token))
+ return "Комната не найдена или переполнена", 404
+
+ return render_template_string('''
+
+
+
+
+ Панель управления
+
+
+
+
+
+
Добро пожаловать, {{ session['username'] }}
+
+
+
+
+
+''', session=session)
+
+
+@app.route('/logout', methods=['POST'])
+def logout():
+ session.pop('username', None)
+ return redirect(url_for('index'))
+
+
+@app.route('/room/')
+def room(token):
+ if 'username' not in session:
+ return redirect(url_for('index'))
+
+ if token not in rooms:
+ return redirect(url_for('dashboard'))
+
+ is_admin = rooms[token]['admin'] == session['username']
+ current_game = rooms[token].get('current_game')
+
+ return render_template_string('''
+
+
+
+
+
+ Комната {{ token }}
+
-
-
-
-
-
- Все категории
- {% for category in categories %}
- {{ category }}
- {% endfor %}
-
-
-
-
-
- {% for product in products %}
-
- {% if product.get('photos') and product['photos']|length > 0 %}
-
-
-
- {% endif %}
-
{{ product['name'] }}
-
{{ product['price'] }} с
-
{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}
-
Подробнее
-
В корзину
-
- {% endfor %}
-
-
+
+ }
+ @media (prefers-color-scheme: dark) {
+ :root {
+ --background-color: #121212;
+ --surface-color: #1e1e1e;
+ --text-color: #ffffff;
+ --control-bg-color: #333;
+ --control-icon-color: #eee;
+ }
+ }
+
+
+
+
+
+
+
+ Комната: {{ token }}
+ Пользователи: {% for user in rooms[token]['users'] %}{{ user }}{% if not loop.last %}, {% endif %}{% endfor %}
-
-
-
+
+
+
+ Покинуть комнату
-
-
-
- ×
-
Укажите количество и цвет
-
-
- Добавить
-
+
-
-
-
-
-
×
-
Корзина
-
-
- Итого: 0 с
- Очистить
- Заказать
-
-
+
+
+
+
Доступные игры
+
+ {% for game_id, game_info in games_data.items() %}
+ {% if rooms[token]['users']|length >= game_info.min_players and rooms[token]['users']|length <= game_info.max_players %}
+
+
{{ game_info.name }}
+
{{ game_info.description }}
+ {% if is_admin %}
+
Начать {{ game_info.name }}
+ {% endif %}
+
+ {% endif %}
+ {% endfor %}
+
+
+
-
🛒
-
-
-
-
-
-
-
- '''
- return render_template_string(catalog_html, products=products, categories=categories, repo_id=REPO_ID)
-
-@app.route('/product/
')
-def product_detail(index):
- data = load_data()
- products = data['products']
- try:
- product = products[index]
- except IndexError:
- return "Продукт не найден", 404
- detail_html = '''
-
-
{{ product['name'] }}
-
-
- {% if product.get('photos') %}
- {% for photo in product['photos'] %}
-
-
-
-
-
- {% endfor %}
- {% else %}
-
-
-
- {% endif %}
-
-
-
-
-
-
Категория: {{ product.get('category', 'Без категории') }}
-
Цена: {{ product['price'] }} с
-
Описание: {{ product['description'] }}
-
Доступные цвета: {{ product.get('colors', ['Нет цветов'])|join(', ') }}
-
- '''
- return render_template_string(detail_html, product=product, repo_id=REPO_ID)
+ const startTurnButton = document.createElement('button');
+ startTurnButton.textContent = 'Начать ход';
+ startTurnButton.classList.add('game-button');
+ startTurnButton.id = 'start-turn-button';
+ gameContent.appendChild(startTurnButton);
-@app.route('/admin', methods=['GET', 'POST'])
-def admin():
- data = load_data()
- products = data['products']
- categories = data['categories']
+ const guessInput = document.createElement('input');
+ guessInput.type = 'text';
+ guessInput.id = 'crocodile-guess-input';
+ guessInput.classList.add('game-input');
+ guessInput.placeholder = 'Ваша догадка';
+ guessInput.disabled = true;
+ gameContent.appendChild(guessInput);
- if request.method == 'POST':
- action = request.form.get('action')
-
- if action == 'add_category':
- category_name = request.form.get('category_name')
- if category_name and category_name not in categories:
- categories.append(category_name)
- save_data(data)
- return redirect(url_for('admin'))
- return "Ошибка: Категория уже существует или не указано название", 400
-
- elif action == 'delete_category':
- category_index = int(request.form.get('category_index'))
- deleted_category = categories.pop(category_index)
- for product in products:
- if product.get('category') == deleted_category:
- product['category'] = 'Без категории'
- save_data(data)
- return redirect(url_for('admin'))
-
- elif action == 'add':
- name = request.form.get('name')
- price = request.form.get('price')
- description = request.form.get('description')
- category = request.form.get('category')
- photos_files = request.files.getlist('photos')
- colors = request.form.getlist('colors')
- photos_list = []
-
- if photos_files:
- for photo in photos_files[:10]: # Ограничение до 10 фото
- if photo and photo.filename:
- photo_filename = secure_filename(photo.filename)
- uploads_dir = 'uploads'
- os.makedirs(uploads_dir, exist_ok=True)
- temp_path = os.path.join(uploads_dir, photo_filename)
- photo.save(temp_path)
- api = HfApi()
- api.upload_file(
- path_or_fileobj=temp_path,
- path_in_repo=f"photos/{photo_filename}",
- repo_id=REPO_ID,
- repo_type="dataset",
- token=HF_TOKEN_WRITE,
- commit_message=f"Добавлено фото для товара {name}"
- )
- photos_list.append(photo_filename)
- if os.path.exists(temp_path):
- os.remove(temp_path)
-
- if not name or not price or not description:
- return "Ошибка: Заполните все обязательные поля", 400
-
- price = float(price.replace(',', '.'))
- new_product = {
- 'name': name,
- 'price': price,
- 'description': description,
- 'category': category if category in categories else 'Без категории',
- 'photos': photos_list,
- 'colors': colors if colors else []
- }
- products.append(new_product)
- save_data(data)
- return redirect(url_for('admin'))
-
- elif action == 'edit':
- index = int(request.form.get('index'))
- name = request.form.get('name')
- price = request.form.get('price')
- description = request.form.get('description')
- category = request.form.get('category')
- photos_files = request.files.getlist('photos')
- colors = request.form.getlist('colors')
-
- if photos_files and any(photo.filename for photo in photos_files):
- new_photos_list = []
- for photo in photos_files[:10]: # Ограничение до 10 фото
- if photo and photo.filename:
- photo_filename = secure_filename(photo.filename)
- uploads_dir = 'uploads'
- os.makedirs(uploads_dir, exist_ok=True)
- temp_path = os.path.join(uploads_dir, photo_filename)
- photo.save(temp_path)
- api = HfApi()
- api.upload_file(
- path_or_fileobj=temp_path,
- path_in_repo=f"photos/{photo_filename}",
- repo_id=REPO_ID,
- repo_type="dataset",
- token=HF_TOKEN_WRITE,
- commit_message=f"Обновлено фото для товара {name}"
- )
- new_photos_list.append(photo_filename)
- if os.path.exists(temp_path):
- os.remove(temp_path)
- products[index]['photos'] = new_photos_list
-
- products[index]['name'] = name
- products[index]['price'] = float(price.replace(',', '.'))
- products[index]['description'] = description
- products[index]['category'] = category if category in categories else 'Без категории'
- products[index]['colors'] = colors if colors else []
- save_data(data)
- return redirect(url_for('admin'))
-
- elif action == 'delete':
- index = int(request.form.get('index'))
- del products[index]
- save_data(data)
- return redirect(url_for('admin'))
-
- admin_html = '''
-
-
-
-
-
- Админ-панель
-
-
-
-
-
-
-
Добавление товара
-
-
-
Управление категориями
-
-
- Название категории:
-
- Добавить
-
-
-
Список категорий
-
- {% for category in categories %}
-
-
{{ category }}
-
-
-
- Удалить
-
-
- {% endfor %}
-
-
-
Управление базой данных
-
- Создать копию
-
-
- Скачать базу
-
-
-
Список товаров
-
- {% for product in products %}
-
-
{{ product['name'] }}
-
Категория: {{ product.get('category', 'Без категории') }}
-
Цена: {{ product['price'] }} с
-
Описание: {{ product['description'] }}
-
Цвета: {{ product.get('colors', ['Нет цветов'])|join(', ') }}
- {% if product.get('photos') and product['photos']|length > 0 %}
-
- {% for photo in product['photos'] %}
-
- {% endfor %}
-
- {% endif %}
-
- Редактировать
-
-
-
- Название:
-
- Цена:
-
- Описание:
- {{ product['description'] }}
- Категория:
-
- Без категории
- {% for category in categories %}
- {{ category }}
- {% endfor %}
-
- Фотографии (до 10):
-
- Цвета:
-
- Добавить цвет
- Сохранить
-
-
-
-
-
- Удалить
-
-
- {% endfor %}
-
-
-
-
-
- '''
- return render_template_string(admin_html, products=products, categories=categories, repo_id=REPO_ID)
-
-@app.route('/backup', methods=['POST'])
-def backup():
- upload_db_to_hf()
- return "Резервная копия создана.", 200
-
-@app.route('/download', methods=['GET'])
-def download():
- download_db_from_hf()
- return "База данных скачана.", 200
+ window.location.href = '/dashboard';
+ }
+
+
+