| from flask import Flask, render_template_string, request, redirect, url_for |
| import json |
| import os |
| import logging |
| import threading |
| import time |
| from datetime import datetime |
| from huggingface_hub import HfApi, hf_hub_download |
| from huggingface_hub.utils import RepositoryNotFoundError |
|
|
| app = Flask(__name__) |
| DATA_FILE = 'products.json' |
|
|
| |
| REPO_ID = "Testbase1/testsett" |
| HF_TOKEN_WRITE = os.getenv("HF_TOKEN") |
| HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") |
|
|
| |
| logging.basicConfig(level=logging.DEBUG) |
|
|
| |
| def load_data(): |
| """Загружает данные из JSON-файла или скачивает их из репозитория Hugging Face.""" |
| try: |
| |
| download_db_from_hf() |
| with open(DATA_FILE, 'r', encoding='utf-8') as file: |
| return json.load(file) |
| except FileNotFoundError: |
| logging.warning("Локальный файл базы данных не найден после скачивания.") |
| return [] |
| except json.JSONDecodeError: |
| logging.error("Ошибка: Невозможно декодировать JSON файл.") |
| return [] |
| except RepositoryNotFoundError: |
| logging.error("Репозиторий не найден. Создание локальной базы данных.") |
| return [] |
| except Exception as e: |
| logging.error(f"Произошла ошибка при загрузке данных: {e}") |
| return [] |
|
|
|
|
| def save_data(data): |
| """Сохраняет данные в JSON-файл.""" |
| try: |
| with open(DATA_FILE, 'w', encoding='utf-8') as file: |
| json.dump(data, file, ensure_ascii=False, indent=4) |
| except Exception as e: |
| logging.error(f"Ошибка при сохранении данных: {e}") |
| raise |
|
|
| |
| def upload_db_to_hf(): |
| """Загружает JSON-файл базы данных в репозиторий Hugging Face.""" |
| 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.") |
| except Exception as e: |
| logging.error(f"Ошибка при загрузке резервной копии: {e}") |
|
|
| def download_db_from_hf(): |
| """Скачивает JSON-файл базы данных из репозитория Hugging Face.""" |
| 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 |
| except Exception as e: |
| logging.error(f"Ошибка при скачивании JSON базы: {e}") |
| raise |
|
|
| def periodic_backup(): |
| """Периодически вызывает функцию upload_db_to_hf() каждые 100 секунд.""" |
| while True: |
| upload_db_to_hf() |
| time.sleep(15) |
|
|
| |
| @app.route('/') |
| def catalog(): |
| products = load_data() |
| catalog_html = ''' |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Каталог</title> |
| <style> |
| body { |
| font-family: Arial, sans-serif; |
| margin: 20px; |
| background-color: #f9f9f9; |
| } |
| h1 { |
| color: #333; |
| } |
| .product { |
| background-color: #fff; |
| border: 1px solid #ddd; |
| padding: 15px; |
| margin-bottom: 10px; |
| border-radius: 5px; |
| } |
| .product h2 { |
| margin-top: 0; |
| color: #555; |
| } |
| .product p { |
| color: #777; |
| } |
| </style> |
| </head> |
| <body> |
| <h1>Каталог товаров</h1> |
| {% for product in products %} |
| <div class="product"> |
| <h2>{{ product['name'] }}</h2> |
| <p><strong>Цена:</strong> {{ product['price'] }} руб.</p> |
| <p><strong>Описание:</strong> {{ product['description'] }}</p> |
| </div> |
| {% endfor %} |
| </body> |
| </html> |
| ''' |
| return render_template_string(catalog_html, products=products) |
|
|
| @app.route('/admin', methods=['GET', 'POST']) |
| def admin(): |
| if request.method == 'POST': |
| name = request.form.get('name') |
| price = request.form.get('price') |
| description = request.form.get('description') |
|
|
| logging.debug(f"Полученные данные из формы: name={name}, price={price}, description={description}") |
|
|
| if name and price and description: |
| try: |
| price = float(price.replace(',', '.')) |
| except ValueError: |
| logging.error("Ошибка: Цена должна быть числом.") |
| return "Ошибка: Цена должна быть числом.", 400 |
|
|
| products = load_data() |
| products.append({ |
| 'name': name, |
| 'price': price, |
| 'description': description |
| }) |
|
|
| try: |
| save_data(products) |
| except Exception as e: |
| return f"Ошибка при сохранении данных: {e}", 500 |
|
|
| return redirect(url_for('admin')) |
|
|
| products = load_data() |
| admin_html = ''' |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Админ-панель</title> |
| <style> |
| body { |
| font-family: Arial, sans-serif; |
| margin: 20px; |
| background-color: #f9f9f9; |
| } |
| h1 { |
| color: #333; |
| } |
| form { |
| background-color: #fff; |
| padding: 20px; |
| border: 1px solid #ddd; |
| border-radius: 5px; |
| max-width: 400px; |
| } |
| label { |
| display: block; |
| margin-top: 10px; |
| color: #555; |
| } |
| input, textarea { |
| width: 100%; |
| padding: 8px; |
| margin-top: 5px; |
| border: 1px solid #ddd; |
| border-radius: 4px; |
| } |
| button { |
| margin-top: 15px; |
| padding: 10px 15px; |
| background-color: #28a745; |
| color: white; |
| border: none; |
| border-radius: 4px; |
| cursor: pointer; |
| } |
| button:hover { |
| background-color: #218838; |
| } |
| </style> |
| </head> |
| <body> |
| <h1>Добавление товара</h1> |
| <form method="POST"> |
| <label for="name">Название товара:</label> |
| <input type="text" id="name" name="name" required> |
| |
| <label for="price">Цена:</label> |
| <input type="number" id="price" name="price" step="0.01" required> |
| |
| <label for="description">Описание:</label> |
| <textarea id="description" name="description" rows="4" required></textarea> |
| |
| <button type="submit">Добавить товар</button> |
| </form> |
| |
| <h2>Управление базой данных</h2> |
| |
| <!-- Кнопки для резервной копии и скачивания --> |
| <form method="POST" action="{{ url_for('backup') }}"> |
| <button type="submit">Создать резервную копию</button> |
| </form> |
| |
| <form method="GET" action="{{ url_for('download') }}"> |
| <button type="submit">Скачать базу данных</button> |
| </form> |
| |
| </body> |
| </html> |
| ''' |
| return render_template_string(admin_html) |
|
|
| @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 |
|
|
| if __name__ == '__main__': |
| |
| backup_thread = threading.Thread(target=periodic_backup, daemon=True) |
| backup_thread.start() |
|
|
| |
| try: |
| load_data() |
| except Exception as e: |
| logging.error(f"Не удалось загрузить базу данных при запуске: {e}") |
| |
| |
|
|
| app.run(debug=True, host='0.0.0.0', port=7860) |
|
|