import os import io import base64 import json import logging import threading import time from datetime import datetime, timedelta import random import string from flask import Flask, render_template_string, request, redirect, url_for, flash, make_response, jsonify from huggingface_hub import HfApi, hf_hub_download from huggingface_hub.utils import RepositoryNotFoundError, HfHubHTTPError from dotenv import load_dotenv import requests load_dotenv() app = Flask(__name__) app.secret_key = 'your_unique_secret_key_gippo_312_shop_54321_no_login_synkris' DATA_FILE = 'data.json' DATA_FILE_TEMP = 'data.json.tmp' SYNC_FILES = [DATA_FILE] REPO_ID = "Kgshop/neurospace" HF_TOKEN_WRITE = os.getenv("HF_TOKEN") HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") DOWNLOAD_RETRIES = 3 DOWNLOAD_DELAY = 5 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') def download_db_from_hf(specific_file=None, retries=DOWNLOAD_RETRIES, delay=DOWNLOAD_DELAY): if not HF_TOKEN_READ and not HF_TOKEN_WRITE: return False token_to_use = HF_TOKEN_READ if HF_TOKEN_READ else HF_TOKEN_WRITE files_to_download = [specific_file] if specific_file else SYNC_FILES all_successful = True for file_name in files_to_download: success = False for attempt in range(retries + 1): try: hf_hub_download( repo_id=REPO_ID, filename=file_name, repo_type="dataset", token=token_to_use, local_dir=".", local_dir_use_symlinks=False, force_download=True, resume_download=False ) success = True break except RepositoryNotFoundError: all_successful = False break except HfHubHTTPError as e: if e.response.status_code == 404: if attempt == 0 and not os.path.exists(file_name): try: if file_name == DATA_FILE: with open(file_name, 'w', encoding='utf-8') as f: json.dump({}, f) except Exception: pass success = True break else: pass except Exception: pass if attempt < retries: time.sleep(delay) if not success: all_successful = False return all_successful def upload_db_to_hf(specific_file=None): if not HF_TOKEN_WRITE: return try: api = HfApi() files_to_upload = [specific_file] if specific_file else SYNC_FILES for file_name in files_to_upload: if os.path.exists(file_name): try: api.upload_file( path_or_fileobj=file_name, path_in_repo=file_name, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN_WRITE, commit_message=f"Sync {file_name} {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" ) except Exception: pass except Exception: pass def periodic_backup(): backup_interval = 1800 while True: time.sleep(backup_interval) upload_db_to_hf() def load_data(): data = {} if os.path.exists(DATA_FILE): try: with open(DATA_FILE, 'r', encoding='utf-8') as f: data = json.load(f) except json.JSONDecodeError: if download_db_from_hf(specific_file=DATA_FILE): try: with open(DATA_FILE, 'r', encoding='utf-8') as f: data = json.load(f) except (FileNotFoundError, json.JSONDecodeError): data = {} elif download_db_from_hf(specific_file=DATA_FILE): try: with open(DATA_FILE, 'r', encoding='utf-8') as f: data = json.load(f) except (FileNotFoundError, json.JSONDecodeError): data = {} if not isinstance(data, dict): data = {} return data def save_data(data): try: with open(DATA_FILE_TEMP, 'w', encoding='utf-8') as file: json.dump(data, file, ensure_ascii=False, indent=4) os.replace(DATA_FILE_TEMP, DATA_FILE) upload_db_to_hf(specific_file=DATA_FILE) except Exception: if os.path.exists(DATA_FILE_TEMP): os.remove(DATA_FILE_TEMP) LANDING_PAGE_TEMPLATE = ''' MetaStore - AI система для Вашего Бизнеса ''' ADMHOSTO_TEMPLATE = ''' Админ-панель

Управление Средами

{% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% for category, message in messages %}
{{ message }}
{% endfor %} {% endif %} {% endwith %}
{% if active_environments %} {% else %}
Список активных сред пуст
{% endif %}

Архив

{% if archived_environments %} {% else %}
Архив пуст
{% endif %}
''' SYNKRIS_LOOK_TEMPLATE = ''' NeuroSpace

NeuroSpace

''' @app.route('/') def index(): return render_template_string(LANDING_PAGE_TEMPLATE) @app.route('/admhosto', methods=['GET']) def admhosto(): data = load_data() active_environments = [] archived_environments = [] for env_id, env_data in data.items(): if not isinstance(env_data, dict): continue env_item = { "id": env_id, "keyword": env_data.get("keyword", "N/A"), "type": env_data.get("type", "closed"), "hits": env_data.get("hits", 0), "created_at": env_data.get("created_at", ""), "link": url_for('serve_env', env_id=env_id, _external=True) } if env_data.get("archived"): archived_environments.append(env_item) else: active_environments.append(env_item) active_environments.sort(key=lambda x: x.get('created_at', ''), reverse=True) archived_environments.sort(key=lambda x: x.get('created_at', ''), reverse=True) return render_template_string(ADMHOSTO_TEMPLATE, active_environments=active_environments, archived_environments=archived_environments) @app.route('/admhosto/create', methods=['POST']) def create_environment(): all_data = load_data() keyword = request.form.get('keyword', '').strip() env_type = request.form.get('env_type', 'closed') if not keyword: flash('Ключевое слово не может быть пустым.', 'error') return redirect(url_for('admhosto')) while True: new_id = ''.join(random.choices(string.digits, k=6)) if new_id not in all_data: break all_data[new_id] = { "keyword": keyword, "type": env_type, "device_token": None, "hits": 0, "logs": [], "created_at": datetime.utcnow().isoformat(), "archived": False } save_data(all_data) flash(f'Новая {env_type} среда с ID {new_id} создана.', 'success') return redirect(url_for('admhosto')) @app.route('/admhosto/delete/', methods=['POST']) def delete_environment(env_id): all_data = load_data() if env_id in all_data: all_data[env_id]['archived'] = True save_data(all_data) flash(f'Среда {env_id} перемещена в архив.', 'success') else: flash(f'Среда {env_id} не найдена.', 'error') return redirect(url_for('admhosto')) @app.route('/admhosto/restore/', methods=['POST']) def restore_environment(env_id): all_data = load_data() if env_id in all_data: all_data[env_id]['archived'] = False save_data(all_data) flash(f'Среда {env_id} восстановлена из архива.', 'success') else: flash(f'Среда {env_id} не найдена.', 'error') return redirect(url_for('admhosto')) @app.route('/admhosto/clear_user/', methods=['POST']) def clear_user(env_id): all_data = load_data() if env_id in all_data and all_data[env_id].get('type') == 'closed': all_data[env_id]['device_token'] = None save_data(all_data) flash(f'Пользователь отвязан от среды {env_id}.', 'success') else: flash(f'Ошибка: Среда не найдена или не является закрытой.', 'error') return redirect(url_for('admhosto')) @app.route('/admhosto/toggle_type/', methods=['POST']) def toggle_type(env_id): all_data = load_data() if env_id in all_data: current_type = all_data[env_id].get('type', 'closed') if current_type == 'closed': all_data[env_id]['type'] = 'open' flash(f'Среда {env_id} теперь открыта.', 'success') else: all_data[env_id]['type'] = 'closed' all_data[env_id]['device_token'] = None flash(f'Среда {env_id} теперь закрыта. Пользователь сброшен.', 'success') save_data(all_data) else: flash(f'Среда {env_id} не найдена.', 'error') return redirect(url_for('admhosto')) @app.route('/admhosto/stats/') def get_env_stats(env_id): data = load_data() env_data = data.get(env_id) if not env_data: return jsonify({"error": "Среда не найдена"}), 404 raw_logs = env_data.get("logs", []) formatted_logs = [] for log in reversed(raw_logs): try: utc_dt = datetime.fromisoformat(log['time']) almaty_dt = utc_dt + timedelta(hours=5) time_str = almaty_dt.strftime('%Y-%m-%d %H:%M:%S') formatted_logs.append({ "time": time_str, "ip": log.get('ip', 'unknown'), "ua": log.get('ua', 'unknown') }) except: continue response_data = { "id": env_id, "keyword": env_data.get("keyword"), "type": env_data.get("type", "closed"), "hits": env_data.get("hits", 0), "logs": formatted_logs } return jsonify(response_data) @app.route('/env/') def serve_env(env_id): data = load_data() env_data = data.get(env_id) if not env_data or not isinstance(env_data, dict) or env_data.get("archived"): return "Среда не найдена или заархивирована.", 404 keyword = env_data.get("keyword", "") env_type = env_data.get("type", "closed") current_log = { "time": datetime.utcnow().isoformat(), "ip": request.headers.get('X-Forwarded-For', request.remote_addr), "ua": request.headers.get('User-Agent', '')[:150] } env_data['hits'] = env_data.get('hits', 0) + 1 if 'logs' not in env_data or not isinstance(env_data.get('logs'), list): env_data['logs'] = [] env_data['logs'].append(current_log) if len(env_data['logs']) > 30: env_data['logs'] = env_data['logs'][-30:] data[env_id] = env_data save_data(data) if env_type == 'open': return render_template_string(SYNKRIS_LOOK_TEMPLATE, keyword=keyword) stored_token = env_data.get("device_token") user_token = request.cookies.get(f'access_token_{env_id}') if stored_token: if user_token != stored_token: return """ Доступ запрещен

⛔ Доступ запрещен

Эта ссылка уже привязана к другому устройству или браузеру.

""", 403 return render_template_string(SYNKRIS_LOOK_TEMPLATE, keyword=keyword) else: new_token = ''.join(random.choices(string.ascii_letters + string.digits, k=40)) env_data['device_token'] = new_token data[env_id] = env_data save_data(data) resp = make_response(render_template_string(SYNKRIS_LOOK_TEMPLATE, keyword=keyword)) resp.set_cookie(f'access_token_{env_id}', new_token, max_age=31536000, httponly=True, samesite='Lax') return resp if __name__ == '__main__': download_db_from_hf() if HF_TOKEN_WRITE: backup_thread = threading.Thread(target=periodic_backup, daemon=True) backup_thread.start() else: logging.info("HF_TOKEN_WRITE is not set. Periodic backup is disabled.") port = int(os.environ.get('PORT', 7860)) app.run(debug=False, host='0.0.0.0', port=port)