diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -5,21 +5,24 @@ import os import logging import threading import time -from datetime import datetime, timedelta +from datetime import datetime from huggingface_hub import HfApi, hf_hub_download from werkzeug.utils import secure_filename import random +import string app = Flask(__name__) app.secret_key = os.getenv("FLASK_SECRET_KEY", "supersecretkey") -DATA_FILE = 'data_adusis.json' -REPO_ID = "Eluza133/A12d12s12" +DATA_FILE = 'cloud_data.json' +REPO_ID = "Eluza133/Z1e1u" # Используем тот же репозиторий HF_TOKEN_WRITE = os.getenv("HF_TOKEN") HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE +ADMIN_PASSWORD = "87132morflot" cache = Cache(app, config={'CACHE_TYPE': 'simple'}) logging.basicConfig(level=logging.INFO) +# Функции из вашего кода для работы с базой данных и Hugging Face @cache.memoize(timeout=300) def load_data(): try: @@ -28,20 +31,14 @@ def load_data(): data = json.load(file) if not isinstance(data, dict): logging.warning("Данные не в формате dict, инициализация пустой базы") - return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}} - data.setdefault('posts', []) + return {'users': {}, 'files': {}} data.setdefault('users', {}) - data.setdefault('general_chat', []) - data.setdefault('private_chats', {}) - for user in data['users']: - data['users'][user].setdefault('last_chat_visit', '1970-01-01 00:00:00') - data['users'][user].setdefault('last_private_visit', '1970-01-01 00:00:00') - data['users'][user].setdefault('last_seen', '1970-01-01 00:00:00') + data.setdefault('files', {}) logging.info("Данные успешно загружены") return data except Exception as e: logging.error(f"Ошибка при загрузке данных: {e}") - return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}} + return {'users': {}, 'files': {}} def save_data(data): try: @@ -84,357 +81,145 @@ def download_db_from_hf(): logging.error(f"Ошибка при скачивании базы данных: {e}") if not os.path.exists(DATA_FILE): with open(DATA_FILE, 'w', encoding='utf-8') as f: - json.dump({'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}, f) + json.dump({'users': {}, 'files': {}}, f) def periodic_backup(): while True: upload_db_to_hf() - time.sleep(1800) + time.sleep(1800) # Бэкап каждые 30 минут -def get_unread_count(data, username): - if username not in data['users']: - return 0 - last_visit = datetime.strptime(data['users'][username]['last_chat_visit'], '%Y-%m-%d %H:%M:%S') - return sum(1 for msg in data['general_chat'] if datetime.strptime(msg['time'], '%Y-%m-%d %H:%M:%S') > last_visit) - -def get_private_unread_count(data, username): - if username not in data['users']: - return 0 - last_visit = datetime.strptime(data['users'][username]['last_private_visit'], '%Y-%m-%d %H:%M:%S') - unread = 0 - for chat_key, messages in data['private_chats'].items(): - users = chat_key.split('_') - if username in users: - unread += sum(1 for msg in messages if datetime.strptime(msg['time'], '%Y-%m-%d %H:%M:%S') > last_visit and msg['sender'] != username) - return unread - -def is_user_online(data, username): - if username not in data['users']: - return False - last_seen = datetime.strptime(data['users'][username]['last_seen'], '%Y-%m-%d %H:%M:%S') - return (datetime.now() - last_seen).total_seconds() < 300 - -def update_last_seen(data, username): - if username in data['users']: - data['users'][username]['last_seen'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - save_data(data) +# Генерация 13-значного токена +def generate_token(): + return ''.join(random.choices(string.ascii_letters + string.digits, k=13)) +# Базовый стиль CSS BASE_STYLE = ''' :root { --primary: #ff4d6d; --secondary: #00ddeb; --accent: #8b5cf6; - --background-light: #f7f9fc; - --background-dark: #1e1b2e; - --card-bg: rgba(255, 255, 255, 0.97); - --card-bg-dark: rgba(40, 35, 60, 0.97); + --background-light: #f5f6fa; + --background-dark: #1a1625; + --card-bg: rgba(255, 255, 255, 0.95); + --card-bg-dark: rgba(40, 35, 60, 0.95); --text-light: #2a1e5a; --text-dark: #e8e1ff; - --shadow: 0 12px 40px rgba(0, 0, 0, 0.15); - --glass-bg: rgba(255, 255, 255, 0.2); - --transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); - --online: #34c759; - --offline: #ff3b30; + --shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + --glass-bg: rgba(255, 255, 255, 0.15); + --transition: all 0.3s ease; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Inter', sans-serif; background: var(--background-light); color: var(--text-light); - line-height: 1.7; - overflow-x: hidden; + line-height: 1.6; } body.dark { background: var(--background-dark); color: var(--text-dark); } -.sidebar { - position: fixed; - top: 0; - left: 0; - width: 300px; - height: 100%; - background: var(--glass-bg); - backdrop-filter: blur(15px); - padding: 30px; +.container { + margin: 20px auto; + max-width: 900px; + padding: 25px; + background: var(--card-bg); + border-radius: 20px; box-shadow: var(--shadow); - z-index: 1000; - transition: transform var(--transition); -} -.sidebar.hidden { - transform: translateX(-100%); } -.sidebar-header { - display: flex; - align-items: center; - gap: 15px; - margin-bottom: 40px; +body.dark .container { + background: var(--card-bg-dark); } -.nav-brand { +h1 { font-size: 2em; - font-weight: 900; + font-weight: 800; + text-align: center; + margin-bottom: 25px; background: linear-gradient(135deg, var(--primary), var(--accent)); -webkit-background-clip: text; color: transparent; } -.logo { - width: 45px; - height: 45px; - border-radius: 16px; - box-shadow: var(--shadow); - transition: transform var(--transition); -} -.logo:hover { - transform: scale(1.1); -} -.nav-links { - display: flex; - flex-direction: column; - gap: 15px; -} -.nav-link { - display: flex; - align-items: center; - gap: 15px; - padding: 15px 25px; - background: var(--card-bg); +input { + width: 100%; + padding: 14px; + margin: 12px 0; + border: none; + border-radius: 14px; + background: var(--glass-bg); color: var(--text-light); - text-decoration: none; - border-radius: 16px; font-size: 1.1em; - font-weight: 600; - transition: var(--transition); - position: relative; + box-shadow: inset 0 3px 10px rgba(0, 0, 0, 0.1); } -body.dark .nav-link { - background: var(--card-bg-dark); +body.dark input { color: var(--text-dark); } -.nav-link:hover { - transform: translateX(8px); - background: var(--primary); - color: white; - box-shadow: 0 8px 25px rgba(255, 77, 109, 0.5); -} -.nav-link .badge { - position: absolute; - right: 15px; - background: var(--secondary); - color: white; - padding: 5px 12px; - border-radius: 14px; - font-size: 0.85em; - font-weight: 700; -} -.logout-btn { - background: var(--secondary); - color: white; -} -.logout-btn:hover { - background: #00b8c5; -} -.menu-btn { - display: none; - font-size: 30px; - background: var(--glass-bg); - border: none; - color: var(--primary); - cursor: pointer; - position: fixed; - top: 20px; - left: 20px; - z-index: 1001; - padding: 12px; - border-radius: 50%; - box-shadow: var(--shadow); - transition: var(--transition); -} -.menu-btn:hover { - background: var(--primary); - color: white; -} -.container { - margin: 20px auto 20px 320px; - max-width: 1200px; - padding: 30px; - transition: var(--transition); +input:focus { + outline: none; + box-shadow: 0 0 0 4px var(--primary); } .btn { - padding: 15px 30px; + padding: 14px 28px; background: var(--primary); color: white; border: none; - border-radius: 16px; + border-radius: 14px; cursor: pointer; font-size: 1.1em; font-weight: 600; transition: var(--transition); - display: inline-flex; - align-items: center; - gap: 10px; box-shadow: var(--shadow); } .btn:hover { transform: scale(1.05); background: #e6415f; - box-shadow: 0 10px 30px rgba(255, 77, 109, 0.6); -} -input, textarea, select { - width: 100%; - padding: 15px; - margin: 12px 0; - border: none; - border-radius: 16px; - background: var(--glass-bg); - color: var(--text-light); - font-size: 1.1em; - transition: var(--transition); - box-shadow: inset 0 3px 10px rgba(0, 0, 0, 0.1); -} -body.dark input, body.dark textarea, body.dark select { - color: var(--text-dark); } -input:focus, textarea:focus, select:focus { - outline: none; - background: rgba(255, 255, 255, 0.25); - box-shadow: 0 0 0 4px var(--primary); -} -.modal { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.9); - z-index: 2000; - justify-content: center; - align-items: center; - transition: opacity var(--transition); +.flash { + color: var(--secondary); + text-align: center; + margin-bottom: 15px; } -.modal img, .modal video { - max-width: 95%; - max-height: 95%; - object-fit: contain; - border-radius: 20px; - box-shadow: var(--shadow); - animation: zoomIn 0.4s ease; +.file-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 20px; + margin-top: 20px; } -.theme-toggle { - position: fixed; - top: 20px; - right: 20px; - background: var(--glass-bg); - border: none; - padding: 12px; - border-radius: 50%; - cursor: pointer; - font-size: 26px; +.file-item { + background: var(--card-bg); + padding: 15px; + border-radius: 16px; box-shadow: var(--shadow); - transition: var(--transition); -} -.theme-toggle:hover { - transform: rotate(90deg); - background: var(--accent); - color: white; -} -.status-dot { - width: 12px; - height: 12px; - border-radius: 50%; - display: inline-block; - margin-left: 8px; + text-align: center; } -.online { background: var(--online); } -.offline { background: var(--offline); } -@keyframes zoomIn { - from { opacity: 0; transform: scale(0.85); } - to { opacity: 1; transform: scale(1); } +body.dark .file-item { + background: var(--card-bg-dark); } -@media (max-width: 900px) { - .sidebar { - width: 100%; - max-width: 300px; - transform: translateX(-100%); - } - .sidebar.active { - transform: translateX(0); - } - .menu-btn { - display: block; - } - .container { - margin: 80px 20px 20px 20px; - max-width: calc(100% - 40px); - } - .theme-toggle { - top: 80px; - } +.file-item a { + color: var(--primary); + text-decoration: none; } -@media (max-width: 480px) { - .nav-brand { font-size: 1.6em; } - .nav-link { font-size: 1em; padding: 12px 20px; } - .btn { padding: 12px 20px; font-size: 1em; } - .logo { width: 40px; height: 40px; } +.file-item a:hover { + color: var(--accent); } ''' -NAV_HTML = ''' - -''' - -@app.route('/register', methods=['GET', 'POST']) +# Регистрация через /admhosto +@app.route('/admhosto', methods=['GET', 'POST']) def register(): if request.method == 'POST': - username = request.form.get('username') password = request.form.get('password') - data = load_data() - - if username in data['users']: - flash('Пользователь уже существует!') + if password == ADMIN_PASSWORD: + token = generate_token() + data = load_data() + data['users'][token] = { + 'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'files': [] + } + save_data(data) + flash(f'Ваш токен: {token}. Сохраните его!') return redirect(url_for('register')) - - data['users'][username] = { - 'password': password, - 'bio': '', - 'link': '', - 'avatar': None, - 'last_chat_visit': '1970-01-01 00:00:00', - 'last_private_visit': '1970-01-01 00:00:00', - 'last_seen': datetime.now().strftime('%Y-%m-%d %H:%M:%S') - } - save_data(data) - flash('Регистрация успешна! Пожалуйста, войдите.') - return redirect(url_for('login')) - - is_authenticated = 'username' in session - username = session.get('username', None) - data = load_data() - unread_count = get_unread_count(data, username) if is_authenticated else 0 - private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0 - user_count = len(data['users']) - is_online = is_user_online(data, username) if is_authenticated else False + else: + flash('Неверный пароль!') html = ''' @@ -442,63 +227,13 @@ def register():
- - -