diff --git "a/app.py" "b/app.py"
--- "a/app.py"
+++ "b/app.py"
@@ -1,54 +1,60 @@
-from flask import Flask, render_template_string, request, redirect, url_for, session, flash, send_file, jsonify
+from flask import Flask, render_template_string, request, redirect, url_for, session, flash, jsonify
from flask_caching import Cache
import json
import os
import logging
import threading
import time
-from datetime import datetime
+from datetime import datetime, timedelta
from huggingface_hub import HfApi, hf_hub_download
from werkzeug.utils import secure_filename
-import requests
-from io import BytesIO
+import random
app = Flask(__name__)
app.secret_key = os.getenv("FLASK_SECRET_KEY", "supersecretkey")
-DATA_FILE = 'cloud_data.json'
-REPO_ID = "Eluza133/Z1e1u"
+DATA_FILE = 'data_adusis.json'
+REPO_ID = "Eluza133/A12d12s12" # Replace with your actual repo ID
HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE
-REGISTRATION_CODE = "morshenalphacl"
-cache = Cache(app, config={'CACHE_TYPE': 'simple'})
+cache = Cache(app, config={'CACHE_TYPE': 'simple'}) # Consider 'filesystem' for larger apps
logging.basicConfig(level=logging.INFO)
-# Функции для работы с базой данных и Hugging Face
-@cache.memoize(timeout=300)
+# --- Helper Functions ---
+
+@cache.memoize(timeout=600) # Cache for longer, 10 minutes
def load_data():
try:
download_db_from_hf()
with open(DATA_FILE, 'r', encoding='utf-8') as file:
data = json.load(file)
+ # Data validation and defaults (more robust)
if not isinstance(data, dict):
- logging.warning("Данные не в формате dict, инициализация пустой базы")
- return {'users': {}, 'files': {}}
+ logging.warning("Data is not a dictionary. Initializing empty database.")
+ return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}
+ data.setdefault('posts', [])
data.setdefault('users', {})
- data.setdefault('files', {})
- logging.info("Данные успешно загружены")
+ data.setdefault('general_chat', [])
+ data.setdefault('private_chats', {})
+ for user in data['users']:
+ data['users'][user].setdefault('last_chat_visit', '1970-01-01T00:00:00') # ISO 8601
+ data['users'][user].setdefault('last_private_visit', '1970-01-01T00:00:00')
+ data['users'][user].setdefault('last_seen', '1970-01-01T00:00:00')
+ logging.info("Data loaded successfully.")
return data
except Exception as e:
- logging.error(f"Ошибка при загрузке данных: {e}")
- return {'users': {}, 'files': {}}
+ logging.error(f"Error loading data: {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")
+ cache.clear() # Clear cache on every save
+ logging.info("Data saved and uploaded to HF.")
except Exception as e:
- logging.error(f"Ошибка при сохранении данных: {e}")
+ logging.error(f"Error saving data: {e}")
raise
def upload_db_to_hf():
@@ -60,11 +66,11 @@ def upload_db_to_hf():
repo_id=REPO_ID,
repo_type="dataset",
token=HF_TOKEN_WRITE,
- commit_message=f"Бэкап {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
+ commit_message=f"Backup {datetime.now().isoformat()}" # ISO 8601
)
- logging.info("База данных загружена на Hugging Face")
+ logging.info("Database uploaded to Hugging Face.")
except Exception as e:
- logging.error(f"Ошибка при загрузке базы данных: {e}")
+ logging.error(f"Error uploading database: {e}")
def download_db_from_hf():
try:
@@ -76,786 +82,2484 @@ def download_db_from_hf():
local_dir=".",
local_dir_use_symlinks=False
)
- logging.info("База данных скачана с Hugging Face")
+ logging.info("Database downloaded from Hugging Face.")
except Exception as e:
- logging.error(f"Ошибка при скачивании базы данных: {e}")
+ logging.error(f"Error downloading database: {e}")
if not os.path.exists(DATA_FILE):
+ logging.info("Creating initial data file.")
with open(DATA_FILE, 'w', encoding='utf-8') as f:
- json.dump({'users': {}, 'files': {}}, f)
+ json.dump({'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}, f, indent=4)
+
def periodic_backup():
while True:
upload_db_to_hf()
- time.sleep(1800)
+ time.sleep(1800) # 30 minutes
+
+def get_unread_count(data, username):
+ if username not in data['users']:
+ return 0
+ last_visit = datetime.fromisoformat(data['users'][username]['last_chat_visit'])
+ return sum(1 for msg in data['general_chat'] if datetime.fromisoformat(msg['time']) > last_visit)
+
+def get_private_unread_count(data, username):
+ if username not in data['users']:
+ return 0
+ last_visit = datetime.fromisoformat(data['users'][username]['last_private_visit'])
+ 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.fromisoformat(msg['time']) > 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.fromisoformat(data['users'][username]['last_seen'])
+ return (datetime.now() - last_seen).total_seconds() < 300 # 5 minutes
-def get_file_type(filename):
- video_extensions = ('.mp4', '.mov', '.avi')
- image_extensions = ('.jpg', '.jpeg', '.png', '.gif')
- if filename.lower().endswith(video_extensions):
- return 'video'
- elif filename.lower().endswith(image_extensions):
- return 'image'
- return 'other'
+def update_last_seen(data, username):
+ if username in data['users']:
+ data['users'][username]['last_seen'] = datetime.now().isoformat() # Use ISO 8601
+ save_data(data)
+
+
+
+# --- Styling and HTML Templates ---
BASE_STYLE = '''
:root {
- --primary: #ff4d6d;
- --secondary: #00ddeb;
- --accent: #8b5cf6;
- --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 10px 30px rgba(0, 0, 0, 0.2);
- --glass-bg: rgba(255, 255, 255, 0.15);
+ /* 2025 Modern UI Colors */
+ --primary: #6A1B9A; /* Deep Purple */
+ --secondary: #00838F; /* Cyan */
+ --accent: #FFCA28; /* Amber */
+ --background-light: #FAFAFA; /* Very Light Gray */
+ --background-dark: #212121; /* Dark Gray */
+ --card-bg: rgba(255, 255, 255, 0.9);
+ --card-bg-dark: rgba(50, 50, 50, 0.9);
+ --text-light: #424242; /* Dark Gray */
+ --text-dark: #EEEEEE; /* Light Gray */
+ --shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ --glass-bg: rgba(255, 255, 255, 0.2);
--transition: all 0.3s ease;
- --delete-color: #ff4444;
+ --online: #4CAF50; /* Green */
+ --offline: #F44336; /* Red */
+ --border-radius: 12px;
}
+/* Reset & Basic Styles */
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
- font-family: 'Inter', sans-serif;
+ font-family: 'Inter', sans-serif; /* Modern Font */
background: var(--background-light);
color: var(--text-light);
line-height: 1.6;
+ overflow-x: hidden;
}
-body.dark {
- background: var(--background-dark);
- color: var(--text-dark);
-}
-.container {
- margin: 20px auto;
- max-width: 1200px;
- padding: 25px;
- background: var(--card-bg);
- border-radius: 20px;
+body.dark { background: var(--background-dark); color: var(--text-dark); }
+
+/* Sidebar */
+.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);
+ border-right: 1px solid rgba(0, 0, 0, 0.1);
}
-body.dark .container {
- background: var(--card-bg-dark);
+.sidebar.hidden { transform: translateX(-100%); }
+.sidebar-header {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 30px;
}
-h1 {
- font-size: 2em;
- font-weight: 800;
- text-align: center;
- margin-bottom: 25px;
+.nav-brand {
+ font-size: 1.7em;
+ font-weight: 700;
background: linear-gradient(135deg, var(--primary), var(--accent));
-webkit-background-clip: text;
color: transparent;
+ letter-spacing: 1px;
}
-h2 {
- font-size: 1.5em;
- margin-top: 30px;
+.logo {
+ width: 40px; height: 40px;
+ border-radius: var(--border-radius);
+ box-shadow: var(--shadow);
+}
+.nav-links { display: flex; flex-direction: column; gap: 10px; }
+.nav-link {
+ display: flex; align-items: center; gap: 12px;
+ padding: 10px 15px;
+ background: var(--card-bg);
color: var(--text-light);
+ text-decoration: none;
+ border-radius: var(--border-radius);
+ font-size: 1em;
+ font-weight: 500;
+ transition: var(--transition);
+ position: relative;
+ border: 1px solid rgba(0, 0, 0, 0.1);
}
-body.dark h2 {
- color: var(--text-dark);
+body.dark .nav-link { background: var(--card-bg-dark); color: var(--text-dark); border-color: rgba(255,255,255,0.1); }
+.nav-link:hover { background: var(--primary); color: white; box-shadow: 0 4px 10px rgba(106, 27, 154, 0.4); }
+.nav-link .badge {
+ position: absolute;
+ right: 15px; top: 50%;
+ transform: translateY(-50%);
+ background: var(--accent);
+ color: white;
+ padding: 3px 8px;
+ border-radius: 8px;
+ font-size: 0.75em; font-weight: 600;
}
-input, textarea {
- width: 100%;
- padding: 14px;
- margin: 12px 0;
- border: none;
- border-radius: 14px;
+.logout-btn { background: var(--secondary); color: white; }
+.logout-btn:hover { background: #006064; /* Darker Cyan */ }
+
+/* Menu Button & Theme Toggle */
+.menu-btn, .theme-toggle {
+ display: none; /* Hidden by default, shown on smaller screens */
+ font-size: 24px;
background: var(--glass-bg);
- color: var(--text-light);
- font-size: 1.1em;
- box-shadow: inset 0 3px 10px rgba(0, 0, 0, 0.1);
-}
-body.dark input, body.dark textarea {
- color: var(--text-dark);
+ border: none;
+ color: var(--primary);
+ cursor: pointer;
+ position: fixed;
+ top: 20px;
+ padding: 10px;
+ border-radius: 50%;
+ box-shadow: var(--shadow);
+ transition: var(--transition);
+ z-index: 1001;
+ border: 1px solid rgba(0, 0, 0, 0.1);
}
-input:focus, textarea:focus {
- outline: none;
- box-shadow: 0 0 0 4px var(--primary);
+.menu-btn { left: 20px; }
+.theme-toggle { right: 20px; }
+.menu-btn:hover, .theme-toggle:hover { background: var(--primary); color: white; }
+
+/* Container & Buttons */
+.container {
+ margin: 20px auto 20px 300px;
+ max-width: 1100px;
+ padding: 25px;
+ transition: var(--transition);
}
.btn {
- padding: 14px 28px;
+ padding: 10px 20px;
background: var(--primary);
color: white;
border: none;
- border-radius: 14px;
+ border-radius: var(--border-radius);
cursor: pointer;
- font-size: 1.1em;
- font-weight: 600;
+ font-size: 1em;
+ font-weight: 500;
transition: var(--transition);
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
box-shadow: var(--shadow);
- display: inline-block;
- text-decoration: none;
+ border: 1px solid rgba(0, 0, 0, 0.1);
}
-.btn:hover {
- transform: scale(1.05);
- background: #e6415f;
-}
-.download-btn {
- background: var(--secondary);
- margin-top: 10px;
-}
-.download-btn:hover {
- background: #00b8c5;
-}
-.delete-btn {
- background: var(--delete-color);
- margin-top: 10px;
-}
-.delete-btn:hover {
- background: #cc3333;
-}
-.flash {
- color: var(--secondary);
- text-align: center;
- margin-bottom: 15px;
-}
-.file-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
- gap: 20px;
- margin-top: 20px;
+.btn:hover { background: #880E4F; /* Darker Purple */ box-shadow: 0 6px 15px rgba(106, 27, 154, 0.5); }
+
+/* Inputs, Textareas, Selects */
+input, textarea, select {
+ width: 100%;
+ padding: 10px;
+ margin: 10px 0;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-radius: var(--border-radius);
+ background: var(--glass-bg);
+ color: var(--text-light);
+ font-size: 1em;
+ transition: var(--transition);
+ box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.1);
}
-.user-list {
- margin-top: 20px;
+body.dark input, body.dark textarea, body.dark select { color: var(--text-dark); border-color: rgba(255,255,255,0.1); }
+input:focus, textarea:focus, select:focus { outline: none; background: rgba(255, 255, 255, 0.3); box-shadow: 0 0 0 3px var(--primary); }
+
+/* Modal */
+.modal {
+ display: none; /* Hidden by default */
+ 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);
}
-.user-item {
- padding: 15px;
- background: var(--card-bg);
- border-radius: 16px;
- margin-bottom: 10px;
+.modal img, .modal video {
+ max-width: 90%; max-height: 90%;
+ object-fit: contain;
+ border-radius: var(--border-radius);
box-shadow: var(--shadow);
- transition: var(--transition);
+ animation: zoomIn 0.3s ease;
}
-body.dark .user-item {
- background: var(--card-bg-dark);
+
+/* Status Dot */
+.status-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-left: 5px; }
+.online { background: var(--online); }
+.offline { background: var(--offline); }
+
+/* Keyframes */
+@keyframes zoomIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
+
+/* Responsive Design */
+@media (max-width: 900px) {
+ .sidebar { width: 100%; max-width: 280px; 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; }
}
-.user-item:hover {
- transform: translateY(-5px);
+@media (max-width: 480px) {
+ .nav-brand { font-size: 1.5em; }
+ .nav-link { font-size: 0.9em; padding: 8px 12px; }
+ .btn { padding: 8px 16px; font-size: 0.9em; }
}
-.user-item a {
- color: var(--primary);
- text-decoration: none;
+
+/* Additional Styles for Forms and Cards */
+h1, h2, h3 {
font-weight: 600;
+ margin-bottom: 15px;
+ background: linear-gradient(135deg, var(--primary), var(--accent));
+ -webkit-background-clip: text;
+ color: transparent;
}
-.user-item a:hover {
- color: var(--accent);
-}
-@media (max-width: 768px) {
- .file-grid {
- grid-template-columns: repeat(2, 1fr);
- }
-}
-@media (max-width: 480px) {
- .file-grid {
- grid-template-columns: 1fr;
- }
-}
-.file-item {
+h1 { font-size: 2em; }
+h2 { font-size: 1.5em; }
+h3 { font-size: 1.2em; }
+
+.form-group { margin-bottom: 20px; }
+.form-group label { display: block; margin-bottom: 5px; font-weight: 500; color: var(--text-light);}
+body.dark .form-group label { color: var(--text-dark); }
+.form-control { width: 100%; } /* Use class for inputs, textareas, etc. */
+
+.card {
background: var(--card-bg);
- padding: 15px;
- border-radius: 16px;
+ border-radius: var(--border-radius);
box-shadow: var(--shadow);
- text-align: center;
+ padding: 20px;
+ margin-bottom: 20px;
transition: var(--transition);
}
-body.dark .file-item {
- background: var(--card-bg-dark);
-}
-.file-item:hover {
- transform: translateY(-5px);
-}
-.file-preview {
- max-width: 100%;
- max-height: 200px;
- object-fit: cover;
- border-radius: 10px;
- margin-bottom: 10px;
- loading: lazy;
-}
-.file-item p {
- font-size: 0.9em;
- margin: 5px 0;
+body.dark .card { background: var(--card-bg-dark); }
+.card:hover { transform: translateY(-5px); }
+
+.flash {
+ color: var(--secondary);
+ text-align: center;
+ margin-bottom: 15px;
+ font-size: 1em;
+ font-weight: 500;
}
-.file-item a {
+
+.username-link {
color: var(--primary);
+ font-weight: 600;
text-decoration: none;
}
-.file-item a:hover {
- color: var(--accent);
-}
-.modal {
- display: none;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.85);
- z-index: 2000;
- justify-content: center;
- align-items: center;
-}
-.modal img, .modal video {
- max-width: 95%;
- max-height: 95%;
- object-fit: contain;
- border-radius: 20px;
- box-shadow: var(--shadow);
-}
-#progress-container {
- width: 100%;
- background: var(--glass-bg);
- border-radius: 10px;
- margin: 15px 0;
- display: none;
-}
-#progress-bar {
- width: 0%;
- height: 20px;
- background: var(--primary);
- border-radius: 10px;
- transition: width 0.3s ease;
-}
+.username-link:hover { color: var(--accent); }
+/* Scrollbar Styling */
+::-webkit-scrollbar { width: 10px; }
+::-webkit-scrollbar-track { background: var(--glass-bg); }
+::-webkit-scrollbar-thumb { background: var(--primary); border-radius: 5px; }
+::-webkit-scrollbar-thumb:hover { background: #880E4F; }
+'''
+
+NAV_HTML = '''
+
'''
+
+# --- Route Handlers ---
+
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
- code = request.form.get('code')
-
data = load_data()
-
+
if username in data['users']:
- flash('Пользователь с таким логином уже существует!')
- return redirect(url_for('register'))
-
- if code != REGISTRATION_CODE:
- flash('Неверный регистрационный код!')
+ flash('User already exists!')
return redirect(url_for('register'))
-
- if not username or not password:
- flash('Логин и пароль обязательны!')
- return redirect(url_for('register'))
-
+
data['users'][username] = {
'password': password,
- 'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
- 'files': []
+ 'bio': '',
+ 'link': '',
+ 'avatar': None,
+ 'last_chat_visit': '1970-01-01T00:00:00', # Use ISO 8601
+ 'last_private_visit': '1970-01-01T00:00:00',
+ 'last_seen': datetime.now().isoformat()
}
save_data(data)
- session['username'] = username
- flash('Регистрация успешна!')
- return redirect(url_for('dashboard'))
-
- html = '''
+ flash('Registration successful! Please login.')
+ 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 = f'''
- Регистрация - Zeus Cloud
+
+
+ Register - Content Hub
-
+
+
+ {NAV_HTML}
+
-
Регистрация в Zeus Cloud
- {% with messages = get_flashed_messages() %}
- {% if messages %}
- {% for message in messages %}
-
{{ message }}
- {% endfor %}
- {% endif %}
- {% endwith %}
-
-
Уже есть аккаунт? Войдите
+
+
Register
+ {{% with messages = get_flashed_messages() %}}
+ {{% if messages %}}
+ {{% for message in messages %}}
+
{{{{ message }}}}
+ {{% endfor %}}
+ {{% endif %}}
+ {{% endwith %}}
+
+
Already have an account? Login
+
+
'''
- return render_template_string(html)
+ 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('/', methods=['GET', 'POST'])
+@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')
- data = load_data()
-
+
if username in data['users'] and data['users'][username]['password'] == password:
session['username'] = username
- return jsonify({'status': 'success', 'redirect': url_for('dashboard')})
- else:
- return jsonify({'status': 'error', 'message': 'Неверный логин или пароль!'})
-
- html = '''
+ session.permanent = True
+ update_last_seen(data, username)
+ return redirect(url_for('feed'))
+ flash('Invalid username or password!')
+ 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 = f'''
- Zeus Cloud
+
+ Login - Content Hub
-
+
+
+ {NAV_HTML}
+
-
Zeus Cloud
-
- {% with messages = get_flashed_messages() %}
- {% if messages %}
- {% for message in messages %}
-
{{ message }}
- {% endfor %}
- {% endif %}
- {% endwith %}
+
+
Login
+ {{% with messages = get_flashed_messages() %}}
+ {{% if messages %}}
+ {{% for message in messages %}}
+
{{{{ message }}}}
+ {{% endfor %}}
+ {{% endif %}}
+ {{% endwith %}}
+
+
Don't have an account? Register
-
-
Нет аккаунта? Зарегистрируйтесь
'''
- return render_template_string(html)
+ 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('/dashboard', methods=['GET', 'POST'])
-def dashboard():
+@app.route('/toggle_like/
', methods=['POST'])
+def toggle_like(post_id):
if 'username' not in session:
- flash('Пожалуйста, войдите!')
- return redirect(url_for('login'))
-
+ 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()
- if username not in data['users']:
- session.pop('username', None)
- flash('Пользователь не найден!')
- return redirect(url_for('login'))
-
- user_files = sorted(data['users'][username]['files'], key=lambda x: x['upload_date'], reverse=True)
+ 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.fromisoformat(x['upload_date']), 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':
- files = request.files.getlist('files')
- if files and len(files) > 20:
- flash('Максимум 20 файлов за раз!')
- return redirect(url_for('dashboard'))
-
- if files:
- os.makedirs('uploads', exist_ok=True)
- api = HfApi()
- temp_files = []
-
- for file in files:
- if file and file.filename:
- filename = secure_filename(file.filename)
- temp_path = os.path.join('uploads', filename)
- file.save(temp_path)
- temp_files.append((temp_path, filename))
-
- for temp_path, filename in temp_files:
- file_path = f"cloud_files/{username}/{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}"
- )
-
- file_info = {
- 'filename': filename,
- 'path': file_path,
- 'type': get_file_type(filename),
- 'upload_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- }
- data['users'][username]['files'].append(file_info)
-
- if os.path.exists(temp_path):
- os.remove(temp_path)
-
- save_data(data)
- flash('Файлы успешно загружены!')
-
- return redirect(url_for('dashboard'))
+ 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 "Post not found", 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().isoformat() # ISO 8601
+ }]
+ save_data(data)
+ return redirect(url_for('feed')) # Redirect after comment
- html = '''
+ elif 'share' in request.form: #For copy Link
+ # copy link logic will now be handled entirely on the client-side
+ return redirect(url_for('feed'))
+
+
+ html = f'''
- Dashboard - Zeus Cloud
+
+
+ Adusis - QoS, BBC, BNWO HUB
-
+
-
-
Zeus Cloud Dashboard
-
Пользователь: {{ username }}
- {% with messages = get_flashed_messages() %}
- {% if messages %}
- {% for message in messages %}
-
{{ message }}
- {% endfor %}
- {% endif %}
- {% endwith %}
-
-
-
Ваши файлы
-
- {% for file in user_files %}
-
- {% if file['type'] == 'video' %}
-
- {% elif file['type'] == 'image' %}
-
![{{ file['filename'] }}](https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ file['path'] }})
- {% else %}
-
{{ file['filename'] }}
- {% endif %}
-
{{ file['upload_date'] }}
-
Скачать
-
Удалить
-
- {% endfor %}
- {% if not user_files %}
-
У вас пока нет загруженных файлов.
+
+ {NAV_HTML}
+
+
+ {% for post in posts %}
+
+ {% if post['type'] == 'video' %}
+
+
+ {% else %}
+
![{{ post['title'] }}](https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ post['type'] }}s/{{ post['filename'] }})
{% endif %}
+
+
{{ post['title'] }}
+
{{ post['description']|truncate(150) }}
+
By: {{ post['uploader'] }}
+
+
+
+ {{ post['likes']|length }}
+
+ {{ post['comments']|length }}
+
+ {{ post['views'] }}
+
+
+
-
Выйти
-
-
+
+
+'''
+ 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)
- // Обработка выхода
- document.getElementById('logout-btn').addEventListener('click', function(e) {
- e.preventDefault();
- localStorage.removeItem('zeusCredentials');
- window.location.href = '/logout';
- });
+@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 "Post not found", 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 #Increment views
+ 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().isoformat() # Use ISO 8601 format
+ }]
+ save_data(data)
+ # Redirect to the same page to prevent form resubmission
+ return redirect(url_for('post_page', post_id=post_id))
+
+ html = f'''
+
+
+
+
+
+
+ {{{{ post['title'] }}}} - Content Hub
+
+
+
+
+
+ {NAV_HTML}
+
+
+
+
{{{{ post['title'] }}}}
+ {{% if post['type'] == 'video' %}}
+
+ {{% else %}}
+
![{{{{ post['title'] }}}}](https://huggingface.co/datasets/{{{{ repo_id }}}}/resolve/main/{{{{ post['type'] }}}}s/{{{{ post['filename'] }}}})
+ {{% 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', []) %}}
+
+ {{% endfor %}}
+
Back to Feed
+ {{% if not is_authenticated %}}
+
Login to like and comment.
+ {{% endif %}}
+
+
+
+
![]()
+
+
'''
- return render_template_string(html, username=username, user_files=user_files, repo_id=REPO_ID)
+ 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),data=data)
-@app.route('/download//')
-def download_file(file_path, filename):
+@app.route('/profile', methods=['GET', 'POST'])
+def profile():
if 'username' not in session:
- flash('Пожалуйста, войдите!')
+ flash('Login to view your profile!')
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.fromisoformat(x['upload_date']), 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:
+ # Find and remove the post
+ 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')) # Redirect after deletion
+
+ 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"Avatar uploaded for {username}"
+ )
+ data['users'][username]['avatar'] = avatar_path
+ # Clean up the temporary file
+ 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')) # Redirect after update
+
+ html = f'''
+
+
+
+
+
+
+ Profile - {{{{ username }}}} - Content Hub
+
+
+
+
+
+ {NAV_HTML}
+
+
+
+
![]()
+
+
+
+
+'''
+ return render_template_string(html,
+ username=username,
+ user_posts=user_posts,
+ bio=bio,
+ link=link,
+ avatar=avatar,
+ 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/', methods=['GET'])
+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']:
- session.pop('username', None)
- flash('Пользователь не найден!')
- return redirect(url_for('login'))
-
- user_files = data['users'][username]['files']
- if not any(file['path'] == file_path for file in user_files):
- flash('У вас нет доступа к этому файлу!')
- return redirect(url_for('dashboard'))
-
- file_url = f"https://huggingface.co/datasets/{REPO_ID}/resolve/main/{file_path}"
- response = requests.get(file_url)
-
- if response.status_code == 200:
- file_content = BytesIO(response.content)
- return send_file(
- file_content,
- as_attachment=True,
- download_name=filename,
- mimetype='application/octet-stream'
- )
- else:
- flash('Ошибка при скачивании файла!')
- return redirect(url_for('dashboard'))
+ return "User not found", 404
+
+ user_posts = sorted([p for p in data['posts'] if p['uploader'] == username], key=lambda x: datetime.fromisoformat(x['upload_date']), 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')
+ user_online = is_user_online(data, username)
+
+ html = f'''
+
+
+
+
+
+
+ {{{{ username }}}}'s Profile - Content Hub
+
+
+
+
+
+ {NAV_HTML}
+
+
+
+
![]()
+
+
+
+
+'''
+ 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,
+ current_user=current_user,
+ repo_id=REPO_ID,
+ unread_count=unread_count,
+ user_count=user_count,
+ private_unread_count=private_unread_count,
+ is_online=is_online,
+ user_online=user_online)
+
+@app.route('/upload', methods=['GET', 'POST'])
+def upload():
if 'username' not in session:
- flash('Пожалуйста, войдите!')
+ flash('Login to upload content!')
return redirect(url_for('login'))
-
- username = session['username']
+
data = load_data()
- if username not in data['users']:
- session.pop('username', None)
- flash('Пользователь не найден!')
- return redirect(url_for('login'))
-
- user_files = data['users'][username]['files']
- file_to_delete = next((file for file in user_files if file['path'] == file_path), None)
-
- if not file_to_delete:
- flash('Файл не найден!')
- return redirect(url_for('dashboard'))
-
- try:
+ 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('Title and file are required!')
+ 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()
- api.delete_file(
+ file_path = f"{file_type}s/{username}/{filename}" # Include username in path
+ 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"Удален файл {file_path} для {username}"
+ commit_message=f"{file_type.capitalize()} uploaded by {username}"
)
- data['users'][username]['files'] = [f for f in user_files if f['path'] != file_path]
+ # Generate unique post ID. Important for avoiding collisions
+ 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}/{filename}", # Store relative path
+ 'type': file_type,
+ 'uploader': username,
+ 'upload_date': datetime.now().isoformat(), # Use ISO 8601
+ 'views': 0,
+ 'likes': [],
+ 'comments': []
+ })
save_data(data)
- flash('Файл успешно удален!')
- except Exception as e:
- logging.error(f"Ошибка при удалении файла: {e}")
- flash('Ошибка при удалении файла!')
-
- return redirect(url_for('dashboard'))
-@app.route('/logout')
-def logout():
- session.pop('username', None)
- return redirect(url_for('login'))
+ if os.path.exists(temp_path):
+ os.remove(temp_path)
+
+ return redirect(url_for('profile')) # Redirect to profile after upload
+
+
+ html = f'''
+
+
+
+
+
+
+ Upload Content - 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,
+ 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 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().isoformat() # Update last visit
+ 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') #Get post
+
+ message_data = {
+ 'sender': username,
+ 'time': datetime.now().isoformat() # Use ISO 8601 format
+ }
+
+ 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}" # Store in chat_files
+ 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"Chat file uploaded by {username}"
+ )
+ message_data['file'] = file_path
+ message_data['file_type'] = file_type
+ if os.path.exists(temp_path):
+ os.remove(temp_path)
+
+ # Handle post sharing
+ 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')) # Redirect after sending
+
+
+ html = f'''
+
+
+
+
+
+
+ General Chat - Content Hub
+
+
+
+
+
+ {NAV_HTML}
+
+
+
+
General Chat
+
+ {{% for message in chat_messages %}}
+
+
+ {{% if message['sender'] == username %}}
+ {{% if avatar %}}
+

+ {{% else %}}
+
+ {{% endif %}}
+ {{% else %}}
+ {{% if data['users'][message['sender']].get('avatar') %}}
+
![{{{{ message['sender'] }}}} Avatar](https://huggingface.co/datasets/{{{{ repo_id }}}}/resolve/main/{{{{ data['users'][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 %}}
+

+ {{% 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 [] # Get user's posts
+ 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('/admhosto')
-def admin_panel():
+@app.route('/users', methods=['GET', 'POST'])
+def users():
data = load_data()
- users = data['users']
-
- html = '''
+ 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 = f'''
- Админ-панель - Zeus Cloud
+
+ Users - Content Hub
-
+
+
+ {NAV_HTML}
+
-
Админ-панель Zeus Cloud
-
Список пользователей
-
- {% for username, user_data in users.items() %}
+
+
Users ({{{{ user_count }}}})
+
+
+
+
+ {{% for user, avatar, online in user_list %}}
+ {{% if user != username %}}
-
{{ username }}
-
Дата регистрации: {{ user_data['created_at'] }}
-
Количество файлов: {{ user_data['files'] | length }}
+ {{% if avatar %}}
+

+ {{% else %}}
+
+ {{% endif %}}
+
- {% endfor %}
- {% if not users %}
-
Пользователей пока нет.
- {% endif %}
+ {{% endif %}}
+ {{% endfor %}}
+
+
'''
- return render_template_string(html, users=users)
+ 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('Login to view messages!')
+ return redirect(url_for('login'))
-@app.route('/admhosto/user/
')
-def admin_user_files(username):
data = load_data()
- if username not in data['users']:
- flash('Пользователь не найден!')
- return redirect(url_for('admin_panel'))
-
- user_files = sorted(data['users'][username]['files'], key=lambda x: x['upload_date'], reverse=True)
-
- html = '''
+ 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 # Get the last message
+ # Count unread messages in this dialog
+ unread = sum(1 for msg in messages if datetime.fromisoformat(msg['time']) > datetime.fromisoformat(data['users'][username]['last_private_visit']) 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) # Check if the other user is online
+ }
+
+ html = f'''
- Файлы пользователя {{ username }} - Zeus Cloud
+
+ Messages - Content Hub
-
+
+
+ {NAV_HTML}
+
-
Файлы пользователя: {{ username }}
- {% with messages = get_flashed_messages() %}
- {% if messages %}
- {% for message in messages %}
-
{{ message }}
- {% endfor %}
- {% endif %}
- {% endwith %}
-
- {% for file in user_files %}
-
- {% if file['type'] == 'video' %}
-
- {% elif file['type'] == 'image' %}
-
![{{ file['filename'] }}](https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ file['path'] }})
- {% else %}
-
{{ file['filename'] }}
- {% endif %}
-
{{ file['upload_date'] }}
-
Скачать
+
+
Messages
+
+ {{% for other_user, info in dialogs.items() %}}
+
+ {{% if info.avatar %}}
+

+ {{% else %}}
+
+ {{% endif %}}
+
+
{{{{ other_user }}}}
+
+ {{% if info.last_message %}}
+
{{{{ info.last_message['time'] }}}}:
+ {{% if 'text' in info.last_message %}}{{{{ info.last_message['text']|truncate(30) }}}}{{% 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 user_files %}
-
У пользователя пока нет файлов.
- {% endif %}
+ {{% endfor %}}
+ {{% if not dialogs %}}
+
No messages yet.
+ {{% endif %}}
+
-
Назад к списку пользователей
-