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(): - - - Register - Content Hub + Регистрация - + - - ''' + NAV_HTML + ''' -
-

Register

+

Регистрация

{% with messages = get_flashed_messages() %} {% if messages %} {% for message in messages %} @@ -507,52 +242,26 @@ def register(): {% endif %} {% endwith %}
- - - + +
-
- ''' - return render_template_string(html, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online) + return render_template_string(html) -@app.route('/login', methods=['GET', 'POST']) +# Главная страница с вводом токена +@app.route('/', methods=['GET', 'POST']) def login(): - data = load_data() if request.method == 'POST': - username = request.form.get('username') - password = request.form.get('password') - - if username in data['users'] and data['users'][username]['password'] == password: - session['username'] = username - session.permanent = True - update_last_seen(data, username) - return redirect(url_for('feed')) - flash('Неверное имя пользователя или пароль!') - return redirect(url_for('login')) - - is_authenticated = 'username' in session - username = session.get('username', None) - if is_authenticated: - update_last_seen(data, username) - 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 + token = request.form.get('token') + data = load_data() + if token in data['users']: + session['token'] = token + return redirect(url_for('dashboard')) + else: + flash('Неверный токен!') html = ''' @@ -560,62 +269,13 @@ def login(): - - Login - Content Hub + Вход - + - - ''' + NAV_HTML + ''' -
-

Login

+

Вход в облако

{% with messages = get_flashed_messages() %} {% if messages %} {% for message in messages %} @@ -624,2143 +284,106 @@ def login(): {% endif %} {% endwith %}
- - - + +
- -
- - - -''' - return render_template_string(html, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online) - -@app.route('/logout') -def logout(): - data = load_data() - username = session.get('username', None) - if username: - update_last_seen(data, username) - session.pop('username', None) - return redirect(url_for('feed')) - -@app.route('/toggle_like/', methods=['POST']) -def toggle_like(post_id): - if 'username' not in session: - return jsonify({'error': 'Not authenticated'}), 401 - data = load_data() - username = session['username'] - post = next((p for p in data['posts'] if p['id'] == post_id), None) - if not post: - return jsonify({'error': 'Post not found'}), 404 - - likes = post.get('likes', []) - if username in likes: - post['likes'] = [u for u in likes if u != username] - liked = False - else: - post['likes'] = likes + [username] - liked = True - save_data(data) - return jsonify({'liked': liked, 'likes_count': len(post['likes'])}) - -@app.route('/increment_view/', methods=['POST']) -def increment_view(post_id): - data = load_data() - post = next((p for p in data['posts'] if p['id'] == post_id), None) - if post: - post['views'] = post.get('views', 0) + 1 - save_data(data) - return jsonify({'views': post['views']}) - return jsonify({'error': 'Post not found'}), 404 - -@app.route('/', methods=['GET', 'POST']) -def feed(): - data = load_data() - username = session.get('username', None) - if username: - update_last_seen(data, username) - posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True) - is_authenticated = 'username' in session - 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 - - if request.method == 'POST' and is_authenticated: - post_id = request.form.get('post_id') - post = next((p for p in data['posts'] if p['id'] == post_id), None) - if not post: - return "Пост не найден", 404 - - if 'comment' in request.form: - comment_text = request.form.get('comment') - if comment_text: - post['comments'] = post.get('comments', []) + [{ - 'user': username, - 'text': comment_text, - 'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S') - }] - save_data(data) - return redirect(url_for('feed')) - - html = ''' - - - - - - - - Adusis - QoS, BBC, BNWO HUB - - - - - - ''' + NAV_HTML + ''' - -
- {% for post in posts %} -
- {% if post['type'] == 'video' %} - - - {% else %} - {{ post['title'] }} - {% endif %} -
-

{{ post['title'] }}

-

{{ post['description']|truncate(150) }}

-

By: {{ post['uploader'] }}

-
-
- - {{ post['likes']|length }} - - {{ post['comments']|length }} - - {{ post['views'] }} -
-
- {% for comment in post.get('comments', []) %} -
- {{ comment['user'] }}: {{ comment['text'] }} -
- {% endfor %} - {% if is_authenticated %} -
- - - -
- {% endif %} -
-
- {% endfor %} -
- - - -''' - return render_template_string(html, - posts=posts, - is_authenticated=is_authenticated, - username=username, - repo_id=REPO_ID, - unread_count=unread_count, - user_count=user_count, - private_unread_count=private_unread_count, - is_online=is_online, - is_user_online=lambda u: is_user_online(data, u)) - -@app.route('/post/', methods=['GET', 'POST']) -def post_page(post_id): - data = load_data() - username = session.get('username', None) - if username: - update_last_seen(data, username) - post = next((p for p in data['posts'] if p['id'] == post_id), None) - if not post: - return "Пост не найден", 404 - - is_authenticated = 'username' in session - 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 - post['views'] = post.get('views', 0) + 1 - save_data(data) - - if request.method == 'POST' and is_authenticated: - if 'comment' in request.form: - comment_text = request.form.get('comment') - if comment_text: - post['comments'] = post.get('comments', []) + [{'user': username, 'text': comment_text, 'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] - save_data(data) - - html = ''' - - - - - - - {{ post['title'] }} - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

{{ post['title'] }}

- {% if post['type'] == 'video' %} - - {% else %} - {{ post['title'] }} - {% endif %} -

{{ post['description'] }}

-

By: {{ post['uploader'] }} | {{ post['upload_date'] }}

-

Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}

- {% if is_authenticated %} - - -
- - -
- {% endif %} -

Comments

- {% for comment in post.get('comments', []) %} -
-

{{ comment['user'] }} ({{ comment['date'] }}): {{ comment['text'] }}

-
- {% endfor %} - Back to Feed - {% if not is_authenticated %} -

Login to like and comment.

- {% endif %} -
- - ''' - return render_template_string(html, post=post, repo_id=REPO_ID, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, is_user_online=lambda u: is_user_online(data, u)) + return render_template_string(html) -@app.route('/profile', methods=['GET', 'POST']) -def profile(): - if 'username' not in session: - flash('Войдите, чтобы просмотреть свой профиль!') +# Личный dashboard +@app.route('/dashboard', methods=['GET', 'POST']) +def dashboard(): + if 'token' not in session: + flash('Пожалуйста, войдите!') return redirect(url_for('login')) + token = session['token'] data = load_data() - username = session['username'] - update_last_seen(data, username) - 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) - is_authenticated = 'username' in session - unread_count = get_unread_count(data, username) - private_unread_count = get_private_unread_count(data, username) - user_count = len(data['users']) - is_online = is_user_online(data, username) - - total_views = sum(post.get('views', 0) for post in user_posts) - total_likes = sum(len(post.get('likes', [])) for post in user_posts) - user_data = data['users'].get(username, {}) - bio = user_data.get('bio', '') - link = user_data.get('link', '') - avatar = user_data.get('avatar', None) - last_seen = user_data.get('last_seen', 'Never') - - if request.method == 'POST': - if 'delete_post' in request.form: - post_id = request.form.get('post_id') - if post_id: - data['posts'] = [p for p in data['posts'] if p['id'] != post_id or p['uploader'] != username] - save_data(data) - return redirect(url_for('profile')) - elif 'update_profile' in request.form: - bio = request.form.get('bio', '') - link = request.form.get('link', '') - avatar_file = request.files.get('avatar') - if avatar_file and avatar_file.filename: - filename = secure_filename(avatar_file.filename) - temp_path = os.path.join('uploads', filename) - os.makedirs('uploads', exist_ok=True) - avatar_file.save(temp_path) - api = HfApi() - avatar_path = f"avatars/{username}/{filename}" - api.upload_file( - path_or_fileobj=temp_path, - path_in_repo=avatar_path, - repo_id=REPO_ID, - repo_type="dataset", - token=HF_TOKEN_WRITE, - commit_message=f"Загружен аватар для {username}" - ) - data['users'][username]['avatar'] = avatar_path - if os.path.exists(temp_path): - os.remove(temp_path) - data['users'][username]['bio'] = bio - data['users'][username]['link'] = link - save_data(data) - return redirect(url_for('profile')) - - html = ''' - - - - - - - Profile - {{ username }} - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-
- {% if avatar %} - Avatar - {% else %} -
- {% endif %} -
-

{{ username }}

-

{{ bio }}

- {% if link %} -

{{ link }}

- {% endif %} -

Total Views: {{ total_views }} | Total Likes: {{ total_likes }}

-

Last Seen: {{ last_seen }}

-
- - - - - -
-
-
-

Posts

-
- {% for post in user_posts %} -
- - {% if post['type'] == 'video' %} - - {% else %} - {{ post['title'] }} - {% endif %} -

{{ post['title'] }}

-
-

{{ post['description']|truncate(100) }}

-

{{ post['upload_date'] }} | Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}

-
- - - -
-
- {% endfor %} - {% if not user_posts %} -

This user hasn't uploaded any posts yet.

- {% endif %} -
-
- - - - -''' - return render_template_string(html, - username=username, - user_posts=user_posts, - bio=bio, - link=link, - avatar=avatar, - total_views=total_views, - total_likes=total_likes, - last_seen=last_seen, - is_authenticated=is_authenticated, - repo_id=REPO_ID, - unread_count=unread_count, - user_count=user_count, - private_unread_count=private_unread_count, - is_online=is_online) - -@app.route('/user/') -def user_profile(username): - data = load_data() - current_user = session.get('username', None) - if current_user: - update_last_seen(data, current_user) - if username not in data['users']: - return "Пользователь не найден", 404 - - 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) - is_authenticated = 'username' in session - unread_count = get_unread_count(data, current_user) if is_authenticated else 0 - private_unread_count = get_private_unread_count(data, current_user) if is_authenticated else 0 - user_count = len(data['users']) - is_online = is_user_online(data, current_user) if is_authenticated else False - - total_views = sum(post.get('views', 0) for post in user_posts) - total_likes = sum(len(post.get('likes', [])) for post in user_posts) - user_data = data['users'].get(username, {}) - bio = user_data.get('bio', '') - link = user_data.get('link', '') - avatar = user_data.get('avatar', None) - last_seen = user_data.get('last_seen', 'Never') - is_user_online_status = is_user_online(data, username) - - html = ''' - - - - - - - {{ username }}'s Profile - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-
- {% if avatar %} - Avatar - {% else %} -
- {% endif %} -
-

{{ username }}

-

{{ bio }}

- {% if link %} -

{{ link }}

- {% endif %} -

Total Views: {{ total_views }} | Total Likes: {{ total_likes }}

-

Last Seen: {{ last_seen }}

- {% if is_authenticated and username != current_user %} - Message - {% endif %} -
-
-

Posts

-
- {% for post in user_posts %} -
- - {% if post['type'] == 'video' %} - - {% else %} - {{ post['title'] }} - {% endif %} -

{{ post['title'] }}

-
-

{{ post['description']|truncate(100) }}

-

{{ post['upload_date'] }} | Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}

-
- {% endfor %} - {% if not user_posts %} -

This user hasn't uploaded any posts yet.

- {% endif %} -
-
- - - - -''' - return render_template_string(html, - username=username, - current_user=current_user, - user_posts=user_posts, - bio=bio, - link=link, - avatar=avatar, - total_views=total_views, - total_likes=total_likes, - last_seen=last_seen, - is_authenticated=is_authenticated, - repo_id=REPO_ID, - unread_count=unread_count, - user_count=user_count, - private_unread_count=private_unread_count, - is_online=is_online, - is_user_online_status=is_user_online_status) - -@app.route('/upload', methods=['GET', 'POST']) -def upload(): - if 'username' not in session: - flash('Войдите, чтобы загрузить контент!') + if token not in data['users']: + session.pop('token', None) + flash('Токен недействителен!') return redirect(url_for('login')) - data = load_data() - username = session['username'] - update_last_seen(data, username) - is_authenticated = 'username' in session - unread_count = get_unread_count(data, username) - private_unread_count = get_private_unread_count(data, username) - user_count = len(data['users']) - is_online = is_user_online(data, username) + user_files = data['users'][token]['files'] if request.method == 'POST': - title = request.form.get('title') - description = request.form.get('description') file = request.files.get('file') - if file and title: + if file and file.filename: filename = secure_filename(file.filename) - file_type = 'video' if filename.endswith(('.mp4', '.mov', '.avi')) else 'image' temp_path = os.path.join('uploads', filename) os.makedirs('uploads', exist_ok=True) file.save(temp_path) api = HfApi() - file_path_in_repo = f"{file_type}s/{username}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}" + file_path = f"cloud_files/{token}/{filename}" api.upload_file( path_or_fileobj=temp_path, - path_in_repo=file_path_in_repo, + path_in_repo=file_path, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN_WRITE, - commit_message=f"Загружен {file_type} от {username}" + commit_message=f"Загружен файл для {token}" ) - post_id = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=10)) - data['posts'].append({ - 'id': post_id, - 'title': title, - 'description': description, - 'uploader': username, - 'filename': file_path_in_repo, - 'type': file_type, - 'upload_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), - 'views': 0, - 'likes': [], - 'comments': [] - }) + file_info = { + 'filename': filename, + 'path': file_path, + 'upload_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S') + } + data['users'][token]['files'].append(file_info) save_data(data) + if os.path.exists(temp_path): os.remove(temp_path) - flash('Контент успешно загружен!') - return redirect(url_for('profile')) - + + return redirect(url_for('dashboard')) + html = ''' - - Upload Content - Content Hub + Dashboard - + - - ''' + NAV_HTML + ''' -
-

Upload Content

- {% with messages = get_flashed_messages() %} - {% if messages %} - {% for message in messages %} -
{{ message }}
- {% endfor %} - {% endif %} - {% endwith %} +

Ваш Dashboard

+

Токен: {{ token }}

- - - - + +
-
- - - -''' - return render_template_string(html, - is_authenticated=is_authenticated, - username=username, - unread_count=unread_count, - user_count=user_count, - private_unread_count=private_unread_count, - is_online=is_online) - -@app.route('/chat', methods=['GET', 'POST']) -def chat(): - data = load_data() - username = session.get('username', None) - if username: - update_last_seen(data, username) - data['users'][username]['last_chat_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - save_data(data) - - is_authenticated = 'username' in session - chat_messages = sorted(data['general_chat'], key=lambda x: datetime.strptime(x['time'], '%Y-%m-%d %H:%M:%S')) - 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 - - if request.method == 'POST' and is_authenticated: - message = request.form.get('message') - file = request.files.get('file') - post_id = request.form.get('post_id') - if message or file or post_id: - msg = { - 'sender': username, - 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') - } - if message: - msg['text'] = message - if post_id: - msg['post_id'] = post_id - if file and file.filename: - filename = secure_filename(file.filename) - temp_path = os.path.join('uploads', filename) - os.makedirs('uploads', exist_ok=True) - file.save(temp_path) - file_type = 'video' if filename.endswith(('.mp4', '.mov', '.avi')) else 'image' - api = HfApi() - file_path = f"chat_files/{username}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}" - api.upload_file( - path_or_fileobj=temp_path, - path_in_repo=file_path, - repo_id=REPO_ID, - repo_type="dataset", - token=HF_TOKEN_WRITE, - commit_message=f"Загружен файл чата от {username}" - ) - msg['file'] = file_path - msg['file_type'] = file_type - if os.path.exists(temp_path): - os.remove(temp_path) - data['general_chat'].append(msg) - save_data(data) - return redirect(url_for('chat')) - - html = ''' - - - - - - - General Chat - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

General Chat

-
- {% for msg in chat_messages %} -
- {% if 'file' in msg %} - {% if msg['file_type'] == 'video' %} - - {% else %} - Chat file - {% endif %} - {% endif %} -
-

{{ msg['sender'] }} ({{ msg['time'] }})

- {% if 'text' in msg %} -

{{ msg['text'] }}

- {% endif %} - {% if 'post_id' in msg %} -

Shared post: View Post

- {% endif %} +

Ваши файлы

+
+ {% for file in user_files %} +
+ {{ file['filename'] }} +

{{ file['upload_date'] }}

-
- {% endfor %} -
- {% if is_authenticated %} -
- - - -
- {% else %} -

Login to send messages.

- {% endif %} -
- - - - -''' - return render_template_string(html, - chat_messages=chat_messages, - is_authenticated=is_authenticated, - username=username, - repo_id=REPO_ID, - unread_count=unread_count, - user_count=user_count, - private_unread_count=private_unread_count, - is_online=is_online, - is_user_online=lambda u: is_user_online(data, u)) - -@app.route('/users') -def users(): - data = load_data() - username = session.get('username', None) - if username: - update_last_seen(data, username) - - is_authenticated = 'username' in session - 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 - users_list = sorted(data['users'].keys()) - - html = ''' - - - - - - - Users - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

Users ({{ user_count }})

-
- {% for user in users_list %} -
- {{ user }} - -

Last seen: {{ data['users'][user]['last_seen'] }}

- {% if is_authenticated and user != username %} - Message - {% endif %} -
{% endfor %} + {% if not user_files %} +

У вас пока нет загруженных файлов.

+ {% endif %}
+ Выйти
- ''' - return render_template_string(html, - users_list=users_list, - data=data, - is_authenticated=is_authenticated, - username=username, - unread_count=unread_count, - user_count=user_count, - private_unread_count=private_unread_count, - is_online=is_online, - is_user_online=lambda u: is_user_online(data, u)) + return render_template_string(html, token=token, user_files=user_files, repo_id=REPO_ID) -@app.route('/messages', methods=['GET', 'POST']) -def messages(): - if 'username' not in session: - flash('Войдите, чтобы просмотреть сообщения!') - return redirect(url_for('login')) - - data = load_data() - username = session['username'] - update_last_seen(data, username) - data['users'][username]['last_private_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - save_data(data) - - is_authenticated = 'username' in session - unread_count = get_unread_count(data, username) - private_unread_count = get_private_unread_count(data, username) - user_count = len(data['users']) - is_online = is_user_online(data, username) - private_chats = {k: v for k, v in data['private_chats'].items() if username in k.split('_')} - - if request.method == 'POST': - recipient = request.form.get('recipient') - message = request.form.get('message') - file = request.files.get('file') - if recipient and (message or file): - if recipient not in data['users']: - flash('Получатель не найден!') - return redirect(url_for('messages')) - chat_key = '_'.join(sorted([username, recipient])) - if chat_key not in data['private_chats']: - data['private_chats'][chat_key] = [] - msg = { - 'sender': username, - 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') - } - if message: - msg['text'] = message - if file and file.filename: - filename = secure_filename(file.filename) - temp_path = os.path.join('uploads', filename) - os.makedirs('uploads', exist_ok=True) - file.save(temp_path) - file_type = 'video' if filename.endswith(('.mp4', '.mov', '.avi')) else 'image' - api = HfApi() - file_path = f"private_files/{chat_key}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}" - api.upload_file( - path_or_fileobj=temp_path, - path_in_repo=file_path, - repo_id=REPO_ID, - repo_type="dataset", - token=HF_TOKEN_WRITE, - commit_message=f"Загружен файл приватного чата между {username} и {recipient}" - ) - msg['file'] = file_path - msg['file_type'] = file_type - if os.path.exists(temp_path): - os.remove(temp_path) - data['private_chats'][chat_key].append(msg) - save_data(data) - return redirect(url_for('messages') + f'#chat_{recipient}') - - html = ''' - - - - - - - Private Messages - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

Private Messages

-
-
- {% for chat_key, messages in private_chats.items() %} - {% set recipient = chat_key.split('_')|reject('eq', username)|first %} - - {% endfor %} -
- - - {% for user in data['users'].keys() %} - {% if user != username %} - - -
-
-
- {% if private_chats %} - {% set active_chat = request.args.get('chat') or (private_chats.keys()|list|first).split('_')|reject('eq', username)|first %} - {% set chat_key = '_'.join(sorted([username, active_chat])) %} - {% set messages = private_chats.get(chat_key, []) %} -

Chat with {{ active_chat }}

-
- {% for msg in messages %} -
- {% if 'file' in msg %} - {% if msg['file_type'] == 'video' %} - - {% else %} - Chat file - {% endif %} - {% endif %} -
-

{{ msg['sender'] }} ({{ msg['time'] }})

- {% if 'text' in msg %} -

{{ msg['text'] }}

- {% endif %} -
-
- {% endfor %} -
-
- - - - -
- {% else %} -

No private chats yet. Start one!

- {% endif %} -
-
-
- - - - -''' - return render_template_string(html, - private_chats=private_chats, - data=data, - is_authenticated=is_authenticated, - username=username, - unread_count=unread_count, - user_count=user_count, - private_unread_count=private_unread_count, - is_online=is_online, - is_user_online=lambda u: is_user_online(data, u)) +# Выход +@app.route('/logout') +def logout(): + session.pop('token', None) + return redirect(url_for('login')) if __name__ == '__main__': threading.Thread(target=periodic_backup, daemon=True).start()