diff --git "a/app.py" "b/app.py" deleted file mode 100644--- "a/app.py" +++ /dev/null @@ -1,3102 +0,0 @@ -from flask import Flask, render_template_string, request, redirect, url_for, session, flash -from flask_caching import Cache -import json -import os -import logging -import threading -import time -from datetime import datetime, timedelta -from huggingface_hub import HfApi, hf_hub_download -from werkzeug.utils import secure_filename -import random - -app = Flask(__name__) -app.secret_key = os.getenv("FLASK_SECRET_KEY", "supersecretkey") -DATA_FILE = 'data_adusis.json' -REPO_ID = "Eluza133/w1f9" -HF_TOKEN_WRITE = os.getenv("HF_TOKEN") -HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE - -cache = Cache(app, config={'CACHE_TYPE': 'simple'}) -logging.basicConfig(level=logging.INFO) - -@cache.memoize(timeout=300) -def load_data(): - try: - download_db_from_hf() - with open(DATA_FILE, 'r', encoding='utf-8') as file: - data = json.load(file) - if not isinstance(data, dict): - logging.warning("Данные не в формате dict, инициализация пустой базы") - return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}} - data.setdefault('posts', []) - 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') - logging.info("Данные успешно загружены") - return data - except Exception as e: - logging.error(f"Ошибка при загрузке данных: {e}") - return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}} - -def save_data(data): - try: - with open(DATA_FILE, 'w', encoding='utf-8') as file: - json.dump(data, file, ensure_ascii=False, indent=4) - upload_db_to_hf() - cache.clear() - logging.info("Данные сохранены и загружены на HF") - except Exception as e: - logging.error(f"Ошибка при сохранении данных: {e}") - raise - -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("База данных загружена на Hugging Face") - except Exception as e: - logging.error(f"Ошибка при загрузке базы данных: {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("База данных скачана с Hugging Face") - except Exception as e: - 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) - -def periodic_backup(): - while True: - upload_db_to_hf() - time.sleep(1800) - -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) - -BASE_STYLE = ''' -:root { - --primary: #ff4d6d; - --secondary: #00ddeb; - --accent: #8b5cf6; - --background-light: #fef9f1; - --background-dark: #1f1a44; - --card-bg: rgba(255, 255, 255, 0.9); - --card-bg-dark: rgba(50, 45, 80, 0.9); - --text-light: #2a1e5a; - --text-dark: #e8e1ff; - --shadow: 0 8px 24px rgba(0, 0, 0, 0.15); - --glass-bg: rgba(255, 255, 255, 0.1); - --transition: all 0.3s ease; - --online: #34c759; - --offline: #ff3b30; -} -* { 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.6; - transition: var(--transition); - overflow-x: hidden; -} -body.dark { - background: var(--background-dark); - color: var(--text-dark); -} -.sidebar { - position: fixed; - top: 0; - left: 0; - width: 280px; - height: 100%; - background: var(--glass-bg); - backdrop-filter: blur(15px); - padding: 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: 12px; - margin-bottom: 30px; -} -.nav-brand { - font-size: 1.6em; - font-weight: 800; - background: linear-gradient(135deg, var(--primary), var(--accent)); - -webkit-background-clip: text; - color: transparent; -} -.logo { - width: 36px; - height: 36px; - border-radius: 12px; - box-shadow: var(--shadow); -} -.nav-links { - display: flex; - flex-direction: column; - gap: 12px; -} -.nav-link { - display: flex; - align-items: center; - gap: 12px; - padding: 12px 20px; - background: var(--card-bg); - color: var(--text-light); - text-decoration: none; - border-radius: 12px; - font-size: 1em; - font-weight: 500; - transition: var(--transition); - position: relative; -} -body.dark .nav-link { - background: var(--card-bg-dark); - color: var(--text-dark); -} -.nav-link:hover { - transform: scale(1.02); - background: var(--primary); - color: white; - box-shadow: 0 4px 16px rgba(255, 77, 109, 0.3); -} -.nav-link .badge { - position: absolute; - right: 12px; - background: var(--secondary); - color: white; - padding: 3px 8px; - border-radius: 10px; - font-size: 0.75em; - font-weight: 700; -} -.logout-btn { - background: var(--secondary); - color: white; -} -.logout-btn:hover { - background: #00b8c5; -} -.menu-btn { - display: none; - font-size: 24px; - background: var(--glass-bg); - border: none; - color: var(--primary); - cursor: pointer; - position: fixed; - top: 15px; - left: 15px; - z-index: 1001; - padding: 10px; - border-radius: 50%; - box-shadow: var(--shadow); - transition: var(--transition); -} -.menu-btn:hover { - background: var(--primary); - color: white; -} -.container { - margin: 20px auto 20px 300px; - max-width: 1200px; - padding: 20px; - transition: var(--transition); -} -.btn { - padding: 12px 24px; - background: var(--primary); - color: white; - border: none; - border-radius: 12px; - cursor: pointer; - font-size: 1em; - font-weight: 600; - transition: var(--transition); - display: inline-flex; - align-items: center; - gap: 8px; - box-shadow: var(--shadow); -} -.btn:hover { - transform: scale(1.03); - background: #e6415f; - box-shadow: 0 6px 20px rgba(255, 77, 109, 0.4); -} -input, textarea, select { - width: 100%; - padding: 12px; - margin: 10px 0; - border: none; - border-radius: 12px; - background: var(--glass-bg); - color: var(--text-light); - font-size: 1em; - transition: var(--transition); - box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.05); -} -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.15); - box-shadow: 0 0 0 3px var(--primary); -} -.modal { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.8); - z-index: 2000; - justify-content: center; - align-items: center; - transition: opacity var(--transition); -} -.modal img, .modal video { - max-width: 90%; - max-height: 90%; - object-fit: contain; - border-radius: 16px; - box-shadow: var(--shadow); - animation: zoomIn 0.3s ease; -} -.theme-toggle { - position: fixed; - top: 15px; - right: 15px; - background: var(--glass-bg); - border: none; - padding: 10px; - border-radius: 50%; - cursor: pointer; - font-size: 20px; - box-shadow: var(--shadow); - transition: var(--transition); -} -.theme-toggle:hover { - transform: rotate(90deg); - background: var(--accent); - color: white; -} -.status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - display: inline-block; - margin-left: 5px; -} -.online { background: var(--online); } -.offline { background: var(--offline); } -@keyframes zoomIn { - from { opacity: 0; transform: scale(0.95); } - to { opacity: 1; transform: scale(1); } -} -@media (max-width: 900px) { - .sidebar { - width: 100%; - max-width: 280px; - transform: translateX(-100%); - } - .sidebar.active { - transform: translateX(0); - } - .menu-btn { - display: block; - } - .container { - margin: 70px 15px 20px 15px; - max-width: calc(100% - 30px); - } - .theme-toggle { - top: 70px; - } -} -@media (max-width: 480px) { - .nav-brand { font-size: 1.4em; } - .nav-link { font-size: 0.9em; padding: 10px 16px; } - .btn { padding: 10px 18px; font-size: 0.9em; } -} -''' - -NAV_HTML = ''' - -''' - -@app.route('/register', 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('Пользователь уже существует!') - 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 - - html = ''' - - - - - - - - Register - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

Register

- {% with messages = get_flashed_messages() %} - {% if messages %} - {% for message in messages %} -
{{ message }}
- {% endfor %} - {% 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('/login', 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 - - html = ''' - - - - - - - Login - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

Login

- {% with messages = get_flashed_messages() %} - {% if messages %} - {% for message in messages %} -
{{ message }}
- {% endfor %} - {% 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('/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 '', 204 - return 'Пост не найден', 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 'like' in request.form: - if username not in post.get('likes', []): - post['likes'] = post.get('likes', []) + [username] - else: - post['likes'] = [u for u in post.get('likes', []) if u != username] - elif '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') - }] - elif 'share' in request.form: - share_to = request.form.get('share_to') - if share_to == 'chat': - data['general_chat'].append({ - 'sender': username, - 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), - 'post_id': post_id - }) - elif share_to in data['users']: - chat_key = f"{min(username, share_to)}_{max(username, share_to)}" - if chat_key not in data['private_chats']: - data['private_chats'][chat_key] = [] - data['private_chats'][chat_key].append({ - 'sender': username, - 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), - 'post_id': post_id - }) - 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(100) }}

-

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), - data=data) - -@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 'like' in request.form: - if username not in post.get('likes', []): - post['likes'] = post.get('likes', []) + [username] - else: - post['likes'] = [user for user in post.get('likes', []) if user != username] - save_data(data) - elif '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)) - -@app.route('/profile', methods=['GET', 'POST']) -def profile(): - if 'username' not in session: - flash('Войдите, чтобы просмотреть свой профиль!') - return redirect(url_for('login')) - - 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 - {% endif %} -
-

{{ username }}

- {% if not is_online %} -

Last seen: {{ last_seen }}

- {% endif %} -

{{ bio }}

- {% if link %} -

{{ link }}

- {% endif %} -

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

- -
-
-

Edit Profile

-
- - - - -
- Add Post -

Your Posts

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

{{ post['title'] }}

-
-

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

-

{{ post['upload_date'] }}

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

You haven't uploaded any posts yet.

- {% endif %} -
-
- - - - -''' - return render_template_string(html, username=username, user_posts=user_posts, total_views=total_views, total_likes=total_likes, bio=bio, link=link, avatar=avatar, repo_id=REPO_ID, is_authenticated=is_authenticated, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, last_seen=last_seen) - -@app.route('/profile/') -def user_profile(username): - data = load_data() - session_username = session.get('username', None) - if session_username: - update_last_seen(data, session_username) - 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, session_username) if is_authenticated else 0 - private_unread_count = get_private_unread_count(data, session_username) if is_authenticated else 0 - user_count = len(data['users']) - is_online = is_user_online(data, session_username) 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) - profile_is_online = is_user_online(data, username) - last_seen = user_data.get('last_seen', 'Never') - - html = ''' - - - - - - - Profile - {{ username }} - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-
- {% if avatar %} - Avatar - {% endif %} -
-

{{ username }}

- {% if not profile_is_online %} -

Last seen: {{ last_seen }}

- {% endif %} -

{{ bio }}

- {% if link %} -

{{ link }}

- {% endif %} -

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

-
-
-

Posts

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

{{ post['title'] }}

-
-

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

-

{{ post['upload_date'] }}

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

No posts yet.

- {% endif %} -
-
- - - - -''' - return render_template_string(html, username=username, user_posts=user_posts, total_views=total_views, total_likes=total_likes, bio=bio, link=link, avatar=avatar, repo_id=REPO_ID, is_authenticated=is_authenticated, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, profile_is_online=profile_is_online, last_seen=last_seen) - -@app.route('/upload', methods=['GET', 'POST']) -def upload(): - if 'username' not in session: - 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) - - if request.method == 'POST': - title = request.form.get('title') - description = request.form.get('description') - file = request.files.get('file') - if not file or not title: - flash('Необходимо указать заголовок и выбрать файл!') - return redirect(url_for('upload')) - - 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.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo' - api = HfApi() - file_path = f"{file_type}s/{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"Uploaded {file_type} by {username}" - ) - - post_id = str(random.randint(100000, 999999)) - while any(p['id'] == post_id for p in data['posts']): - post_id = str(random.randint(100000, 999999)) - - data['posts'].append({ - 'id': post_id, - 'title': title, - 'description': description, - 'filename': f"{username}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{filename}", - 'type': file_type, - 'uploader': username, - 'upload_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), - 'views': 0, - 'likes': [], - 'comments': [] - }) - save_data(data) - if os.path.exists(temp_path): - os.remove(temp_path) - return redirect(url_for('profile')) - - html = ''' - - - - - - - Upload - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

Upload Content

- {% with messages = get_flashed_messages() %} - {% if messages %} - {% for message in messages %} -
{{ message }}
- {% endfor %} - {% endif %} - {% endwith %} -
- - - - -
-
-
-
-
- - - -''' - return render_template_string(html, username=username, is_authenticated=is_authenticated, 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) - is_authenticated = 'username' in session - chat_messages = data['general_chat'] - - if is_authenticated: - data['users'][username]['last_chat_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - save_data(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 - - if request.method == 'POST' and is_authenticated: - message = request.form.get('message') - file = request.files.get('file') - post_id = request.form.get('post_id') - - message_data = { - 'sender': username, - 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') - } - - if message: - message_data['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.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo' - api = HfApi() - file_path = f"chat_files/general/{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"Uploaded chat file by {username}" - ) - message_data['file'] = file_path - message_data['file_type'] = file_type - if os.path.exists(temp_path): - os.remove(temp_path) - - if post_id: - post = next((p for p in data['posts'] if p['id'] == post_id and p['uploader'] == username), None) - if post: - message_data['post_id'] = post_id - - if 'text' in message_data or 'file' in message_data or 'post_id' in message_data: - data['general_chat'].append(message_data) - save_data(data) - - return redirect(url_for('chat')) - - html = ''' - - - - - - - General Chat - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

General Chat

-
- {% for message in chat_messages %} -
- {% if message['sender'] == username %} - {% if avatar %} - Your Avatar - {% else %} -
- {% endif %} - {% else %} - {% if data['users'][message['sender']].get('avatar') %} - {{ message['sender'] }} Avatar - {% else %} -
- {% endif %} - {% endif %} -
- {{ message['sender'] }} - - {% if 'text' in message %} -

{{ message['text'] }}

- {% endif %} - {% if 'file' in message %} - {% if message['file_type'] == 'video' %} - - {% else %} - Chat file - {% endif %} - {% endif %} - {% if 'post_id' in message %} - {% with post = posts|selectattr('id', 'equalto', message['post_id'])|first %} - {% if post %} -

Shared: {{ post['title'] }}

- {% endif %} - {% endwith %} - {% endif %} - {{ message['time'] }} -
-
- {% endfor %} -
- {% if is_authenticated %} -
- - - - -
- {% else %} -

Login to send messages.

- {% endif %} -
- - - - -''' - user_posts = [p for p in data['posts'] if p['uploader'] == username] if is_authenticated else [] - avatar = data['users'][username].get('avatar') if is_authenticated else None - return render_template_string(html, - chat_messages=chat_messages, - username=username, - is_authenticated=is_authenticated, - repo_id=REPO_ID, - posts=data['posts'], - user_posts=user_posts, - 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), - data=data, - avatar=avatar) - -@app.route('/users', methods=['GET', 'POST']) -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 - - search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' else '' - user_list = [(user, data['users'][user].get('avatar'), is_user_online(data, user)) - for user in data['users'] if not search_query or search_query in user.lower()] - - html = ''' - - - - - - - Users - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

Users ({{ user_count }})

-
-
- - -
-
-
- {% for user, avatar, online in user_list %} - {% if user != username %} -
- {% if avatar %} - {{ user }} Avatar - {% else %} -
- {% endif %} - -
- {% endif %} - {% endfor %} -
-
- - - -''' - return render_template_string(html, user_list=user_list, username=username, 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, search_query=search_query) - -@app.route('/messages', methods=['GET']) -def messages(): - if 'username' not in session: - 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) - - dialogs = {} - for chat_key, messages in data['private_chats'].items(): - user1, user2 = chat_key.split('_') - if username not in (user1, user2): - continue - other_user = user1 if user2 == username else user2 - last_message = messages[-1] if messages else None - unread = sum(1 for msg in messages if datetime.strptime(msg['time'], '%Y-%m-%d %H:%M:%S') > datetime.strptime(data['users'][username]['last_private_visit'], '%Y-%m-%d %H:%M:%S') and msg['sender'] != username) - dialogs[other_user] = { - 'last_message': last_message, - 'unread': unread, - 'avatar': data['users'][other_user].get('avatar'), - 'online': is_user_online(data, other_user) - } - - html = ''' - - - - - - - Messages - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

Messages

-
- {% for other_user, info in dialogs.items() %} -
- {% if info.avatar %} - {{ other_user }} Avatar - {% else %} -
- {% endif %} -
- {{ other_user }} - - {% if info.last_message %} -

{{ info.last_message['time'] }}: - {% if 'text' in info.last_message %}{{ info.last_message['text']|truncate(25) }}{% elif 'file' in info.last_message %}File{% elif 'post_id' in info.last_message %}Post{% endif %}

- {% endif %} -
- {% if info.unread > 0 %} - {{ info.unread }} - {% endif %} -
- {% endfor %} - {% if not dialogs %} -

No messages yet.

- {% endif %} -
-
- - - -''' - return render_template_string(html, dialogs=dialogs, username=username, 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('/chat/', methods=['GET', 'POST']) -def private_chat(recipient): - if 'username' not in session: - flash('Войдите, чтобы начать чат!') - return redirect(url_for('login')) - - data = load_data() - username = session['username'] - update_last_seen(data, username) - if recipient not in data['users']: - return "Пользователь не найден", 404 - - 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) - - data['users'][username]['last_private_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - save_data(data) - - chat_key = f"{min(username, recipient)}_{max(username, recipient)}" - if chat_key not in data['private_chats']: - data['private_chats'][chat_key] = [] - private_messages = data['private_chats'][chat_key] - - if request.method == 'POST': - message = request.form.get('message') - file = request.files.get('file') - post_id = request.form.get('post_id') - - message_data = { - 'sender': username, - 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') - } - - if message: - message_data['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.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo' - api = HfApi() - file_path = f"chat_files/private/{chat_key}/{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"Uploaded private chat file by {username} to {recipient}" - ) - message_data['file'] = file_path - message_data['file_type'] = file_type - if os.path.exists(temp_path): - os.remove(temp_path) - - if post_id: - post = next((p for p in data['posts'] if p['id'] == post_id and p['uploader'] == username), None) - if post: - message_data['post_id'] = post_id - - if 'text' in message_data or 'file' in message_data or 'post_id' in message_data: - data['private_chats'][chat_key].append(message_data) - save_data(data) - - return redirect(url_for('private_chat', recipient=recipient)) - - recipient_avatar = data['users'][recipient].get('avatar') - recipient_online = is_user_online(data, recipient) - avatar = data['users'][username].get('avatar') if is_authenticated else None - - html = ''' - - - - - - - Chat with {{ recipient }} - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

- {% if recipient_avatar %} - {{ recipient }} Avatar - {% else %} -
- {% endif %} - Chat with {{ recipient }} -

-
- {% for message in private_messages %} -
- {% if message['sender'] == username %} - {% if avatar %} - Your Avatar - {% else %} -
- {% endif %} - {% else %} - {% if recipient_avatar %} - {{ recipient }} Avatar - {% else %} -
- {% endif %} - {% endif %} -
- {{ message['sender'] }} - - {% if 'text' in message %} -

{{ message['text'] }}

- {% endif %} - {% if 'file' in message %} - {% if message['file_type'] == 'video' %} - - {% else %} - Chat file - {% endif %} - {% endif %} - {% if 'post_id' in message %} - {% with post = posts|selectattr('id', 'equalto', message['post_id'])|first %} - {% if post %} -

Shared: {{ post['title'] }}

- {% endif %} - {% endwith %} - {% endif %} - {{ message['time'] }} -
-
- {% endfor %} -
-
- - - - -
- Back to Messages -
- - - - -''' - user_posts = [p for p in data['posts'] if p['uploader'] == username] - return render_template_string(html, - private_messages=private_messages, - recipient=recipient, - username=username, - is_authenticated=is_authenticated, - repo_id=REPO_ID, - posts=data['posts'], - user_posts=user_posts, - 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), - avatar=avatar, - recipient_avatar=recipient_avatar, - recipient_online=recipient_online) - -@app.route('/admhosto', methods=['GET', 'POST']) -def admin_panel(): - 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 - - search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' and 'search' in request.form else request.args.get('search', '').strip().lower() - if search_query: - posts = [post for post in posts if search_query in post['title'].lower() or search_query in post['description'].lower()] - - if request.method == 'POST' and 'delete' 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] - save_data(data) - return redirect(url_for('admin_panel')) - - html = ''' - - - - - - - Admin Panel - Content Hub - - - - - - ''' + NAV_HTML + ''' - -
-

Admin Panel

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

{{ post['title'] }}

-
-

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

-

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

-

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

-
- - -
-
- {% endfor %} - {% if not posts %} -

No posts found.

- {% endif %} -
-
- - - - -''' - 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), - search_query=search_query) - -if __name__ == '__main__': - threading.Thread(target=periodic_backup, daemon=True).start() - app.run(debug=True, host='0.0.0.0', port=7860) \ No newline at end of file