Update app.py
Browse files
app.py
CHANGED
|
@@ -117,7 +117,7 @@ def register():
|
|
| 117 |
flash('Пользователь уже существует!')
|
| 118 |
return redirect(url_for('register'))
|
| 119 |
|
| 120 |
-
data['users'][username] = {'password': password}
|
| 121 |
save_data(data)
|
| 122 |
logging.info(f"Пользователь {username} зарегистрирован")
|
| 123 |
flash('Регистрация успешна! Войдите в систему.')
|
|
@@ -209,7 +209,7 @@ def login():
|
|
| 209 |
username = session.get('username', None)
|
| 210 |
html = '''
|
| 211 |
<!DOCTYPE html>
|
| 212 |
-
<html lang="
|
| 213 |
<head>
|
| 214 |
<meta charset="UTF-8">
|
| 215 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
@@ -478,7 +478,7 @@ def post_page(post_id):
|
|
| 478 |
|
| 479 |
html = '''
|
| 480 |
<!DOCTYPE html>
|
| 481 |
-
<html lang="
|
| 482 |
<head>
|
| 483 |
<meta charset="UTF-8">
|
| 484 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
@@ -641,17 +641,51 @@ def profile():
|
|
| 641 |
total_views = sum(post.get('views', 0) for post in user_posts)
|
| 642 |
total_likes = sum(len(post.get('likes', [])) for post in user_posts)
|
| 643 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 644 |
if request.method == 'POST':
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 648 |
save_data(data)
|
| 649 |
-
logging.info(f"
|
| 650 |
return redirect(url_for('profile'))
|
| 651 |
|
| 652 |
html = '''
|
| 653 |
<!DOCTYPE html>
|
| 654 |
-
<html lang="
|
| 655 |
<head>
|
| 656 |
<meta charset="UTF-8">
|
| 657 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
@@ -673,13 +707,19 @@ def profile():
|
|
| 673 |
.post-item { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(5px); padding: 15px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); transition: transform 0.3s ease; }
|
| 674 |
.post-item:hover { transform: scale(1.02); }
|
| 675 |
.post-preview { width: 100%; border-radius: 8px; height: 200px; object-fit: cover; cursor: pointer; }
|
| 676 |
-
.btn { display: block; margin: 10px 0; padding: 12px 20px; background: rgba(59, 130, 246, 0.9); color: white; text-decoration: none; border-radius: 8px; border: none; cursor: pointer; transition: all 0.3s ease; }
|
| 677 |
.btn:hover { background: rgba(59, 130, 246, 1); transform: scale(1.05); }
|
| 678 |
.delete-btn { background: rgba(239, 68, 68, 0.9); }
|
| 679 |
.delete-btn:hover { background: rgba(239, 68, 68, 1); }
|
| 680 |
.stats { font-size: 0.9em; color: #666; margin-top: 5px; }
|
| 681 |
.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; }
|
| 682 |
.modal img { max-width: 90%; max-height: 90%; object-fit: contain; transform: scale(1); transition: transform 0.2s ease; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 683 |
@media (max-width: 768px) {
|
| 684 |
.sidebar { transform: translateX(-100%); width: 200px; }
|
| 685 |
.sidebar.active { transform: translateX(0); }
|
|
@@ -690,6 +730,7 @@ def profile():
|
|
| 690 |
.post-preview { height: 150px; }
|
| 691 |
.btn { font-size: 14px; padding: 10px; }
|
| 692 |
h1, h2 { font-size: 1.5em; }
|
|
|
|
| 693 |
}
|
| 694 |
</style>
|
| 695 |
</head>
|
|
@@ -698,7 +739,24 @@ def profile():
|
|
| 698 |
''' + NAV_HTML + '''
|
| 699 |
<div class="container">
|
| 700 |
<h1>Профиль: {{ username }}</h1>
|
| 701 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 702 |
<a href="{{ url_for('upload') }}" class="btn">Добавить публикацию</a>
|
| 703 |
<h2>Ваши публикации</h2>
|
| 704 |
<div class="post-grid">
|
|
@@ -720,7 +778,7 @@ def profile():
|
|
| 720 |
<p class="stats">Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}</p>
|
| 721 |
<form method="POST">
|
| 722 |
<input type="hidden" name="post_id" value="{{ post['id'] }}">
|
| 723 |
-
<button type="submit" class="btn delete-btn">Удалить</button>
|
| 724 |
</form>
|
| 725 |
</div>
|
| 726 |
{% endfor %}
|
|
@@ -753,6 +811,13 @@ def profile():
|
|
| 753 |
}
|
| 754 |
}
|
| 755 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 756 |
document.addEventListener('DOMContentLoaded', function() {
|
| 757 |
const videos = document.querySelectorAll('.post-preview');
|
| 758 |
videos.forEach(video => {
|
|
@@ -808,7 +873,7 @@ def profile():
|
|
| 808 |
</body>
|
| 809 |
</html>
|
| 810 |
'''
|
| 811 |
-
return render_template_string(html, username=username, user_posts=user_posts, total_views=total_views, total_likes=total_likes, repo_id=REPO_ID, is_authenticated=is_authenticated)
|
| 812 |
|
| 813 |
# Страница профиля другого пользователя
|
| 814 |
@app.route('/profile/<username>')
|
|
@@ -825,9 +890,15 @@ def user_profile(username):
|
|
| 825 |
total_views = sum(post.get('views', 0) for post in user_posts)
|
| 826 |
total_likes = sum(len(post.get('likes', [])) for post in user_posts)
|
| 827 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 828 |
html = '''
|
| 829 |
<!DOCTYPE html>
|
| 830 |
-
<html lang="
|
| 831 |
<head>
|
| 832 |
<meta charset="UTF-8">
|
| 833 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
@@ -849,13 +920,17 @@ def user_profile(username):
|
|
| 849 |
.post-item { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(5px); padding: 15px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); transition: transform 0.3s ease; }
|
| 850 |
.post-item:hover { transform: scale(1.02); }
|
| 851 |
.post-preview { width: 100%; border-radius: 8px; height: 200px; object-fit: cover; cursor: pointer; }
|
| 852 |
-
.btn { display: block; margin: 10px 0; padding: 12px 20px; background: rgba(59, 130, 246, 0.9); color: white; text-decoration: none; border-radius: 8px; border: none; cursor: pointer; transition: all 0.3s ease; }
|
| 853 |
.btn:hover { background: rgba(59, 130, 246, 1); transform: scale(1.05); }
|
| 854 |
.stats { font-size: 0.9em; color: #666; margin-top: 5px; }
|
| 855 |
.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; }
|
| 856 |
.modal img { max-width: 90%; max-height: 90%; object-fit: contain; transform: scale(1); transition: transform 0.2s ease; }
|
| 857 |
.username-link { color: #3b82f6; text-decoration: none; font-weight: 600; }
|
| 858 |
.username-link:hover { text-decoration: underline; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 859 |
@media (max-width: 768px) {
|
| 860 |
.sidebar { transform: translateX(-100%); width: 200px; }
|
| 861 |
.sidebar.active { transform: translateX(0); }
|
|
@@ -866,6 +941,7 @@ def user_profile(username):
|
|
| 866 |
.post-preview { height: 150px; }
|
| 867 |
.btn { font-size: 14px; padding: 10px; }
|
| 868 |
h1, h2 { font-size: 1.5em; }
|
|
|
|
| 869 |
}
|
| 870 |
</style>
|
| 871 |
</head>
|
|
@@ -874,7 +950,17 @@ def user_profile(username):
|
|
| 874 |
''' + NAV_HTML + '''
|
| 875 |
<div class="container">
|
| 876 |
<h1>Профиль: {{ username }}</h1>
|
| 877 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 878 |
<h2>Публикации</h2>
|
| 879 |
<div class="post-grid">
|
| 880 |
{% if user_posts %}
|
|
@@ -924,6 +1010,13 @@ def user_profile(username):
|
|
| 924 |
}
|
| 925 |
}
|
| 926 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 927 |
document.addEventListener('DOMContentLoaded', function() {
|
| 928 |
const videos = document.querySelectorAll('.post-preview');
|
| 929 |
videos.forEach(video => {
|
|
@@ -979,7 +1072,7 @@ def user_profile(username):
|
|
| 979 |
</body>
|
| 980 |
</html>
|
| 981 |
'''
|
| 982 |
-
return render_template_string(html, username=username, user_posts=user_posts, total_views=total_views, total_likes=total_likes, repo_id=REPO_ID, is_authenticated=is_authenticated, current_user=current_user)
|
| 983 |
|
| 984 |
# Страница загрузки контента
|
| 985 |
@app.route('/upload', methods=['GET', 'POST'])
|
|
@@ -1057,7 +1150,7 @@ def upload():
|
|
| 1057 |
.sidebar-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
| 1058 |
.nav-brand { font-size: 1.5em; font-weight: 600; color: #3b82f6; }
|
| 1059 |
.nav-links { display: flex; flex-direction: column; gap: 10px; }
|
| 1060 |
-
.nav-link { display: flex; align-items: center; gap: 10px; padding: 12px 15px; background: rgba(59, 130, 246, 0.1); color: #3b82f6; text-decoration: none; border-radius: 8px; transition: all 0.3s ease; }
|
| 1061 |
.nav-link:hover { background: rgba(59, 130, 246, 0.3); color: #2563eb; transform: translateX(5px); }
|
| 1062 |
.logout-btn { background: rgba(239, 68, 68, 0.1); color: #ef4444; }
|
| 1063 |
.logout-btn:hover { background: rgba(239, 68, 68, 0.3); color: #dc2626; }
|
|
@@ -1138,7 +1231,7 @@ def upload():
|
|
| 1138 |
@app.route('/admhosto', methods=['GET', 'POST'])
|
| 1139 |
def admin_panel():
|
| 1140 |
data = load_data()
|
| 1141 |
-
posts = sorted(data.get('posts', []
|
| 1142 |
is_authenticated = 'username' in session
|
| 1143 |
username = session.get('username', None)
|
| 1144 |
|
|
@@ -1157,7 +1250,7 @@ def admin_panel():
|
|
| 1157 |
|
| 1158 |
html = '''
|
| 1159 |
<!DOCTYPE html>
|
| 1160 |
-
<html lang="
|
| 1161 |
<head>
|
| 1162 |
<meta charset="UTF-8">
|
| 1163 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
@@ -1267,4 +1360,4 @@ def admin_panel():
|
|
| 1267 |
if __name__ == '__main__':
|
| 1268 |
backup_thread = threading.Thread(target=periodic_backup, daemon=True)
|
| 1269 |
backup_thread.start()
|
| 1270 |
-
app.run(debug=True, host='0.0.0.0', port=7860)
|
|
|
|
| 117 |
flash('Пользователь уже существует!')
|
| 118 |
return redirect(url_for('register'))
|
| 119 |
|
| 120 |
+
data['users'][username] = {'password': password, 'bio': '', 'link': '', 'avatar': None}
|
| 121 |
save_data(data)
|
| 122 |
logging.info(f"Пользователь {username} зарегистрирован")
|
| 123 |
flash('Регистрация успешна! Войдите в систему.')
|
|
|
|
| 209 |
username = session.get('username', None)
|
| 210 |
html = '''
|
| 211 |
<!DOCTYPE html>
|
| 212 |
+
<html lang="ру">
|
| 213 |
<head>
|
| 214 |
<meta charset="UTF-8">
|
| 215 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
| 478 |
|
| 479 |
html = '''
|
| 480 |
<!DOCTYPE html>
|
| 481 |
+
<html lang="ру">
|
| 482 |
<head>
|
| 483 |
<meta charset="UTF-8">
|
| 484 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
| 641 |
total_views = sum(post.get('views', 0) for post in user_posts)
|
| 642 |
total_likes = sum(len(post.get('likes', [])) for post in user_posts)
|
| 643 |
|
| 644 |
+
# Получение текущих данных пользователя
|
| 645 |
+
user_data = data['users'].get(username, {})
|
| 646 |
+
bio = user_data.get('bio', '')
|
| 647 |
+
link = user_data.get('link', '')
|
| 648 |
+
avatar = user_data.get('avatar', None)
|
| 649 |
+
|
| 650 |
if request.method == 'POST':
|
| 651 |
+
if 'delete_post' in request.form:
|
| 652 |
+
post_id = request.form.get('post_id')
|
| 653 |
+
if post_id:
|
| 654 |
+
data['posts'] = [p for p in data['posts'] if p['id'] != post_id or p['uploader'] != username]
|
| 655 |
+
save_data(data)
|
| 656 |
+
logging.info(f"Публикация {post_id} удалена пользователем {username}")
|
| 657 |
+
return redirect(url_for('profile'))
|
| 658 |
+
elif 'update_profile' in request.form:
|
| 659 |
+
bio = request.form.get('bio', '')
|
| 660 |
+
link = request.form.get('link', '')
|
| 661 |
+
avatar_file = request.files.get('avatar')
|
| 662 |
+
if avatar_file and avatar_file.filename:
|
| 663 |
+
filename = secure_filename(avatar_file.filename)
|
| 664 |
+
temp_path = os.path.join('uploads', filename)
|
| 665 |
+
os.makedirs('uploads', exist_ok=True)
|
| 666 |
+
avatar_file.save(temp_path)
|
| 667 |
+
api = HfApi()
|
| 668 |
+
avatar_path = f"avatars/{username}/{filename}"
|
| 669 |
+
api.upload_file(
|
| 670 |
+
path_or_fileobj=temp_path,
|
| 671 |
+
path_in_repo=avatar_path,
|
| 672 |
+
repo_id=REPO_ID,
|
| 673 |
+
repo_type="dataset",
|
| 674 |
+
token=HF_TOKEN_WRITE,
|
| 675 |
+
commit_message=f"Добавлен аватар для {username}"
|
| 676 |
+
)
|
| 677 |
+
data['users'][username]['avatar'] = avatar_path
|
| 678 |
+
if os.path.exists(temp_path):
|
| 679 |
+
os.remove(temp_path)
|
| 680 |
+
data['users'][username]['bio'] = bio
|
| 681 |
+
data['users'][username]['link'] = link
|
| 682 |
save_data(data)
|
| 683 |
+
logging.info(f"Профиль {username} обновлен")
|
| 684 |
return redirect(url_for('profile'))
|
| 685 |
|
| 686 |
html = '''
|
| 687 |
<!DOCTYPE html>
|
| 688 |
+
<html lang="ру">
|
| 689 |
<head>
|
| 690 |
<meta charset="UTF-8">
|
| 691 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
| 707 |
.post-item { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(5px); padding: 15px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); transition: transform 0.3s ease; }
|
| 708 |
.post-item:hover { transform: scale(1.02); }
|
| 709 |
.post-preview { width: 100%; border-radius: 8px; height: 200px; object-fit: cover; cursor: pointer; }
|
| 710 |
+
.btn { display: inline-block; margin: 10px 0; padding: 12px 20px; background: rgba(59, 130, 246, 0.9); color: white; text-decoration: none; border-radius: 8px; border: none; cursor: pointer; transition: all 0.3s ease; }
|
| 711 |
.btn:hover { background: rgba(59, 130, 246, 1); transform: scale(1.05); }
|
| 712 |
.delete-btn { background: rgba(239, 68, 68, 0.9); }
|
| 713 |
.delete-btn:hover { background: rgba(239, 68, 68, 1); }
|
| 714 |
.stats { font-size: 0.9em; color: #666; margin-top: 5px; }
|
| 715 |
.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; }
|
| 716 |
.modal img { max-width: 90%; max-height: 90%; object-fit: contain; transform: scale(1); transition: transform 0.2s ease; }
|
| 717 |
+
.avatar { width: 100px; height: 100px; border-radius: 50%; object-fit: cover; margin-bottom: 10px; }
|
| 718 |
+
.profile-info { margin-bottom: 20px; }
|
| 719 |
+
textarea { width: 100%; padding: 12px; margin: 10px 0; border: 1px solid #e2e8f0; border-radius: 8px; resize: vertical; background: rgba(255, 255, 255, 0.8); }
|
| 720 |
+
input[type="text"] { width: 100%; padding: 12px; margin: 10px 0; border: 1px solid #e2e8f0; border-radius: 8px; box-sizing: border-box; background: rgba(255, 255, 255, 0.8); }
|
| 721 |
+
.share-btn { background: rgba(16, 185, 129, 0.9); }
|
| 722 |
+
.share-btn:hover { background: rgba(16, 185, 129, 1); }
|
| 723 |
@media (max-width: 768px) {
|
| 724 |
.sidebar { transform: translateX(-100%); width: 200px; }
|
| 725 |
.sidebar.active { transform: translateX(0); }
|
|
|
|
| 730 |
.post-preview { height: 150px; }
|
| 731 |
.btn { font-size: 14px; padding: 10px; }
|
| 732 |
h1, h2 { font-size: 1.5em; }
|
| 733 |
+
.avatar { width: 80px; height: 80px; }
|
| 734 |
}
|
| 735 |
</style>
|
| 736 |
</head>
|
|
|
|
| 739 |
''' + NAV_HTML + '''
|
| 740 |
<div class="container">
|
| 741 |
<h1>Профиль: {{ username }}</h1>
|
| 742 |
+
<div class="profile-info">
|
| 743 |
+
{% if avatar %}
|
| 744 |
+
<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ avatar }}" alt="Аватар" class="avatar">
|
| 745 |
+
{% endif %}
|
| 746 |
+
<p>{{ bio }}</p>
|
| 747 |
+
{% if link %}
|
| 748 |
+
<p><a href="{{ link }}" target="_blank">{{ link }}</a></p>
|
| 749 |
+
{% endif %}
|
| 750 |
+
<p>Всего просмотров: {{ total_views }} | Всего лайков: {{ total_likes }}</p>
|
| 751 |
+
<button class="btn share-btn" onclick="copyProfileLink()">Поделиться профилем</button>
|
| 752 |
+
</div>
|
| 753 |
+
<h2>Редактировать профиль</h2>
|
| 754 |
+
<form method="POST" enctype="multipart/form-data">
|
| 755 |
+
<textarea name="bio" placeholder="Описание профиля" rows="3">{{ bio }}</textarea>
|
| 756 |
+
<input type="text" name="link" placeholder="Ссылка" value="{{ link }}">
|
| 757 |
+
<input type="file" name="avatar" accept="image/*">
|
| 758 |
+
<button type="submit" name="update_profile" class="btn">Сохранить</button>
|
| 759 |
+
</form>
|
| 760 |
<a href="{{ url_for('upload') }}" class="btn">Добавить публикацию</a>
|
| 761 |
<h2>Ваши публикации</h2>
|
| 762 |
<div class="post-grid">
|
|
|
|
| 778 |
<p class="stats">Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}</p>
|
| 779 |
<form method="POST">
|
| 780 |
<input type="hidden" name="post_id" value="{{ post['id'] }}">
|
| 781 |
+
<button type="submit" name="delete_post" class="btn delete-btn">Удалить</button>
|
| 782 |
</form>
|
| 783 |
</div>
|
| 784 |
{% endfor %}
|
|
|
|
| 811 |
}
|
| 812 |
}
|
| 813 |
|
| 814 |
+
function copyProfileLink() {
|
| 815 |
+
const url = window.location.href;
|
| 816 |
+
navigator.clipboard.writeText(url).then(() => {
|
| 817 |
+
alert('Ссылка на профиль скопирована в буфер обмена!');
|
| 818 |
+
});
|
| 819 |
+
}
|
| 820 |
+
|
| 821 |
document.addEventListener('DOMContentLoaded', function() {
|
| 822 |
const videos = document.querySelectorAll('.post-preview');
|
| 823 |
videos.forEach(video => {
|
|
|
|
| 873 |
</body>
|
| 874 |
</html>
|
| 875 |
'''
|
| 876 |
+
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)
|
| 877 |
|
| 878 |
# Страница профиля другого пользователя
|
| 879 |
@app.route('/profile/<username>')
|
|
|
|
| 890 |
total_views = sum(post.get('views', 0) for post in user_posts)
|
| 891 |
total_likes = sum(len(post.get('likes', [])) for post in user_posts)
|
| 892 |
|
| 893 |
+
# Получение данных пользователя
|
| 894 |
+
user_data = data['users'].get(username, {})
|
| 895 |
+
bio = user_data.get('bio', '')
|
| 896 |
+
link = user_data.get('link', '')
|
| 897 |
+
avatar = user_data.get('avatar', None)
|
| 898 |
+
|
| 899 |
html = '''
|
| 900 |
<!DOCTYPE html>
|
| 901 |
+
<html lang="ру">
|
| 902 |
<head>
|
| 903 |
<meta charset="UTF-8">
|
| 904 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
| 920 |
.post-item { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(5px); padding: 15px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); transition: transform 0.3s ease; }
|
| 921 |
.post-item:hover { transform: scale(1.02); }
|
| 922 |
.post-preview { width: 100%; border-radius: 8px; height: 200px; object-fit: cover; cursor: pointer; }
|
| 923 |
+
.btn { display: inline-block; margin: 10px 0; padding: 12px 20px; background: rgba(59, 130, 246, 0.9); color: white; text-decoration: none; border-radius: 8px; border: none; cursor: pointer; transition: all 0.3s ease; }
|
| 924 |
.btn:hover { background: rgba(59, 130, 246, 1); transform: scale(1.05); }
|
| 925 |
.stats { font-size: 0.9em; color: #666; margin-top: 5px; }
|
| 926 |
.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; }
|
| 927 |
.modal img { max-width: 90%; max-height: 90%; object-fit: contain; transform: scale(1); transition: transform 0.2s ease; }
|
| 928 |
.username-link { color: #3b82f6; text-decoration: none; font-weight: 600; }
|
| 929 |
.username-link:hover { text-decoration: underline; }
|
| 930 |
+
.avatar { width: 100px; height: 100px; border-radius: 50%; object-fit: cover; margin-bottom: 10px; }
|
| 931 |
+
.profile-info { margin-bottom: 20px; }
|
| 932 |
+
.share-btn { background: rgba(16, 185, 129, 0.9); }
|
| 933 |
+
.share-btn:hover { background: rgba(16, 185, 129, 1); }
|
| 934 |
@media (max-width: 768px) {
|
| 935 |
.sidebar { transform: translateX(-100%); width: 200px; }
|
| 936 |
.sidebar.active { transform: translateX(0); }
|
|
|
|
| 941 |
.post-preview { height: 150px; }
|
| 942 |
.btn { font-size: 14px; padding: 10px; }
|
| 943 |
h1, h2 { font-size: 1.5em; }
|
| 944 |
+
.avatar { width: 80px; height: 80px; }
|
| 945 |
}
|
| 946 |
</style>
|
| 947 |
</head>
|
|
|
|
| 950 |
''' + NAV_HTML + '''
|
| 951 |
<div class="container">
|
| 952 |
<h1>Профиль: {{ username }}</h1>
|
| 953 |
+
<div class="profile-info">
|
| 954 |
+
{% if avatar %}
|
| 955 |
+
<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ avatar }}" alt="Аватар" class="avatar">
|
| 956 |
+
{% endif %}
|
| 957 |
+
<p>{{ bio }}</p>
|
| 958 |
+
{% if link %}
|
| 959 |
+
<p><a href="{{ link }}" target="_blank">{{ link }}</a></p>
|
| 960 |
+
{% endif %}
|
| 961 |
+
<p>Всего просмотров: {{ total_views }} | Всего лайков: {{ total_likes }}</p>
|
| 962 |
+
<button class="btn share-btn" onclick="copyProfileLink()">Поделиться профилем</button>
|
| 963 |
+
</div>
|
| 964 |
<h2>Публикации</h2>
|
| 965 |
<div class="post-grid">
|
| 966 |
{% if user_posts %}
|
|
|
|
| 1010 |
}
|
| 1011 |
}
|
| 1012 |
|
| 1013 |
+
function copyProfileLink() {
|
| 1014 |
+
const url = window.location.href;
|
| 1015 |
+
navigator.clipboard.writeText(url).then(() => {
|
| 1016 |
+
alert('Ссылка на профиль скопирована в буфер обмена!');
|
| 1017 |
+
});
|
| 1018 |
+
}
|
| 1019 |
+
|
| 1020 |
document.addEventListener('DOMContentLoaded', function() {
|
| 1021 |
const videos = document.querySelectorAll('.post-preview');
|
| 1022 |
videos.forEach(video => {
|
|
|
|
| 1072 |
</body>
|
| 1073 |
</html>
|
| 1074 |
'''
|
| 1075 |
+
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, current_user=current_user)
|
| 1076 |
|
| 1077 |
# Страница загрузки контента
|
| 1078 |
@app.route('/upload', methods=['GET', 'POST'])
|
|
|
|
| 1150 |
.sidebar-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
| 1151 |
.nav-brand { font-size: 1.5em; font-weight: 600; color: #3b82f6; }
|
| 1152 |
.nav-links { display: flex; flex-direction: column; gap: 10px; }
|
| 1153 |
+
.nav-link { display: flex; align-items: center; gap: 10px; padding: 12px 15px; background: rgba(59, 130, 246, 0.1); color: # 3b82f6; text-decoration: none; border-radius: 8px; transition: all 0.3s ease; }
|
| 1154 |
.nav-link:hover { background: rgba(59, 130, 246, 0.3); color: #2563eb; transform: translateX(5px); }
|
| 1155 |
.logout-btn { background: rgba(239, 68, 68, 0.1); color: #ef4444; }
|
| 1156 |
.logout-btn:hover { background: rgba(239, 68, 68, 0.3); color: #dc2626; }
|
|
|
|
| 1231 |
@app.route('/admhosto', methods=['GET', 'POST'])
|
| 1232 |
def admin_panel():
|
| 1233 |
data = load_data()
|
| 1234 |
+
posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
|
| 1235 |
is_authenticated = 'username' in session
|
| 1236 |
username = session.get('username', None)
|
| 1237 |
|
|
|
|
| 1250 |
|
| 1251 |
html = '''
|
| 1252 |
<!DOCTYPE html>
|
| 1253 |
+
<html lang="ру">
|
| 1254 |
<head>
|
| 1255 |
<meta charset="UTF-8">
|
| 1256 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
| 1360 |
if __name__ == '__main__':
|
| 1361 |
backup_thread = threading.Thread(target=periodic_backup, daemon=True)
|
| 1362 |
backup_thread.start()
|
| 1363 |
+
app.run(debug=True, host='0.0.0.0', port=7860)
|