Eluza133 commited on
Commit
26f447d
·
verified ·
1 Parent(s): c1f0bdd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +113 -20
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="ru">
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="ru">
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
- post_id = request.form.get('post_id')
646
- if post_id:
647
- data['posts'] = [p for p in data['posts'] if p['id'] != post_id or p['uploader'] != username]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
648
  save_data(data)
649
- logging.info(f"Публикация {post_id} удалена пользователем {username}")
650
  return redirect(url_for('profile'))
651
 
652
  html = '''
653
  <!DOCTYPE html>
654
- <html lang="ru">
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
- <p>Всего просмотров: {{ total_views }} | Всего лайков: {{ total_likes }}</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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="ru">
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
- <p>Всего просмотров: {{ total_views }} | Всего лайков: {{ total_likes }}</p>
 
 
 
 
 
 
 
 
 
 
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', [] ), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
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="ru">
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)