| from flask import Flask, render_template_string, request, redirect, url_for, session, flash, send_file, jsonify |
| from flask_caching import Cache |
| import json |
| import os |
| import logging |
| import threading |
| import time |
| from datetime import datetime |
| from huggingface_hub import HfApi, hf_hub_download |
| from werkzeug.utils import secure_filename |
| import requests |
| from io import BytesIO |
|
|
| app = Flask(__name__) |
| app.secret_key = os.getenv("FLASK_SECRET_KEY", "supersecretkey") |
| DATA_FILE = 'cloudeng_data.json' |
| REPO_ID = "Eluza133/Z1e1u" |
| HF_TOKEN_WRITE = os.getenv("HF_TOKEN") |
| HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE |
|
|
| 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("Data is not in dict format, initializing empty database") |
| return {'users': {}, 'files': {}} |
| data.setdefault('users', {}) |
| data.setdefault('files', {}) |
| logging.info("Data successfully loaded") |
| return data |
| except Exception as e: |
| logging.error(f"Error loading data: {e}") |
| return {'users': {}, 'files': {}} |
|
|
| 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("Data saved and uploaded to HF") |
| except Exception as e: |
| logging.error(f"Error saving data: {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"Backup {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" |
| ) |
| logging.info("Database uploaded to Hugging Face") |
| except Exception as e: |
| logging.error(f"Error uploading database: {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("Database downloaded from Hugging Face") |
| except Exception as e: |
| logging.error(f"Error downloading database: {e}") |
| if not os.path.exists(DATA_FILE): |
| with open(DATA_FILE, 'w', encoding='utf-8') as f: |
| json.dump({'users': {}, 'files': {}}, f) |
|
|
| def periodic_backup(): |
| while True: |
| upload_db_to_hf() |
| time.sleep(1800) |
|
|
| 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' |
|
|
| 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); |
| --transition: all 0.3s ease; |
| --delete-color: #ff4444; |
| } |
| * { 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; |
| } |
| 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; |
| box-shadow: var(--shadow); |
| } |
| body.dark .container { |
| background: var(--card-bg-dark); |
| } |
| h1 { |
| font-size: 2em; |
| font-weight: 800; |
| text-align: center; |
| margin-bottom: 25px; |
| background: linear-gradient(135deg, var(--primary), var(--accent)); |
| -webkit-background-clip: text; |
| color: transparent; |
| } |
| h2 { |
| font-size: 1.5em; |
| margin-top: 30px; |
| color: var(--text-light); |
| } |
| body.dark h2 { |
| color: var(--text-dark); |
| } |
| input, textarea { |
| width: 100%; |
| padding: 14px; |
| margin: 12px 0; |
| border: none; |
| border-radius: 14px; |
| 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); |
| } |
| input:focus, textarea:focus { |
| outline: none; |
| box-shadow: 0 0 0 4px var(--primary); |
| } |
| .btn { |
| padding: 14px 28px; |
| background: var(--primary); |
| color: white; |
| border: none; |
| border-radius: 14px; |
| cursor: pointer; |
| font-size: 1.1em; |
| font-weight: 600; |
| transition: var(--transition); |
| box-shadow: var(--shadow); |
| display: inline-block; |
| text-decoration: none; |
| } |
| .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; |
| } |
| .user-list { |
| margin-top: 20px; |
| } |
| .user-item { |
| padding: 15px; |
| background: var(--card-bg); |
| border-radius: 16px; |
| margin-bottom: 10px; |
| box-shadow: var(--shadow); |
| transition: var(--transition); |
| } |
| body.dark .user-item { |
| background: var(--card-bg-dark); |
| } |
| .user-item:hover { |
| transform: translateY(-5px); |
| } |
| .user-item a { |
| color: var(--primary); |
| text-decoration: none; |
| font-weight: 600; |
| } |
| .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 { |
| background: var(--card-bg); |
| padding: 15px; |
| border-radius: 16px; |
| box-shadow: var(--shadow); |
| text-align: center; |
| 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; |
| } |
| .file-item a { |
| color: var(--primary); |
| 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; |
| } |
| ''' |
|
|
| @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('A user with this username already exists!') |
| return redirect(url_for('register')) |
| |
| if not username or not password: |
| flash('Username and password are required!') |
| return redirect(url_for('register')) |
| |
| data['users'][username] = { |
| 'password': password, |
| 'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), |
| 'files': [] |
| } |
| save_data(data) |
| session['username'] = username |
| flash('Registration successful!') |
| return redirect(url_for('dashboard')) |
| |
| html = ''' |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Register - Zeus Cloud</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet"> |
| <style>''' + BASE_STYLE + '''</style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Register in Zeus Cloud</h1> |
| {% with messages = get_flashed_messages() %} |
| {% if messages %} |
| {% for message in messages %} |
| <div class="flash">{{ message }}</div> |
| {% endfor %} |
| {% endif %} |
| {% endwith %} |
| <form method="POST" id="register-form"> |
| <input type="text" name="username" placeholder="Enter username" required> |
| <input type="password" name="password" placeholder="Enter password" required> |
| <button type="submit" class="btn">Register</button> |
| </form> |
| <p style="margin-top: 20px;">Already have an account? <a href="{{ url_for('login') }}">Log in</a></p> |
| </div> |
| </body> |
| </html> |
| ''' |
| return render_template_string(html) |
|
|
| @app.route('/', methods=['GET', 'POST']) |
| def login(): |
| 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': 'Invalid username or password!'}) |
| |
| html = ''' |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Zeus Cloud</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet"> |
| <style>''' + BASE_STYLE + '''</style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Zeus Cloud</h1> |
| <div id="flash-messages"> |
| {% with messages = get_flashed_messages() %} |
| {% if messages %} |
| {% for message in messages %} |
| <div class="flash">{{ message }}</div> |
| {% endfor %} |
| {% endif %} |
| {% endwith %} |
| </div> |
| <form method="POST" id="login-form"> |
| <input type="text" name="username" placeholder="Enter username" required> |
| <input type="password" name="password" placeholder="Enter password" required> |
| <button type="submit" class="btn">Log in</button> |
| </form> |
| <p style="margin-top: 20px;">No account? <a href="{{ url_for('register') }}">Register</a></p> |
| </div> |
| <script> |
| // Check localStorage and auto-login |
| const savedCredentials = JSON.parse(localStorage.getItem('zeusCredentials')); |
| if (savedCredentials) { |
| fetch('/', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/x-www-form-urlencoded', |
| }, |
| body: `username=${encodeURIComponent(savedCredentials.username)}&password=${encodeURIComponent(savedCredentials.password)}` |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.status === 'success') { |
| window.location.href = data.redirect; |
| } |
| }) |
| .catch(error => console.error('Auto-login error:', error)); |
| } |
| |
| // Handle login form submission |
| document.getElementById('login-form').addEventListener('submit', function(e) { |
| e.preventDefault(); |
| const formData = new FormData(this); |
| fetch('/', { |
| method: 'POST', |
| body: formData |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.status === 'success') { |
| const username = formData.get('username'); |
| const password = formData.get('password'); |
| localStorage.setItem('zeusCredentials', JSON.stringify({ username, password })); |
| window.location.href = data.redirect; |
| } else { |
| document.getElementById('flash-messages').innerHTML = `<div class="flash">${data.message}</div>`; |
| } |
| }) |
| .catch(error => { |
| document.getElementById('flash-messages').innerHTML = `<div class="flash">Connection error!</div>`; |
| }); |
| }); |
| </script> |
| </body> |
| </html> |
| ''' |
| return render_template_string(html) |
|
|
| @app.route('/dashboard', methods=['GET', 'POST']) |
| def dashboard(): |
| if 'username' not in session: |
| flash('Please log in!') |
| return redirect(url_for('login')) |
| |
| username = session['username'] |
| data = load_data() |
| if username not in data['users']: |
| session.pop('username', None) |
| flash('User not found!') |
| return redirect(url_for('login')) |
| |
| user_files = sorted(data['users'][username]['files'], key=lambda x: x['upload_date'], reverse=True) |
|
|
| if request.method == 'POST': |
| files = request.files.getlist('files') |
| if files and len(files) > 20: |
| flash('Maximum 20 files at a time!') |
| 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"Uploaded file for {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('Files uploaded successfully!') |
| |
| return redirect(url_for('dashboard')) |
|
|
| html = ''' |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Dashboard - Zeus Cloud</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet"> |
| <style>''' + BASE_STYLE + '''</style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Zeus Cloud Dashboard</h1> |
| <p>User: {{ username }}</p> |
| {% with messages = get_flashed_messages() %} |
| {% if messages %} |
| {% for message in messages %} |
| <div class="flash">{{ message }}</div> |
| {% endfor %} |
| {% endif %} |
| {% endwith %} |
| <form id="upload-form" method="POST" enctype="multipart/form-data"> |
| <input type="file" name="files" multiple required accept="image/*,video/*"> |
| <button type="submit" class="btn" id="upload-btn">Upload Files</button> |
| </form> |
| <div id="progress-container"> |
| <div id="progress-bar"></div> |
| </div> |
| <h2 style="margin-top: 30px;">Your Files</h2> |
| <div class="file-grid"> |
| {% for file in user_files %} |
| <div class="file-item"> |
| {% if file['type'] == 'video' %} |
| <video class="file-preview" preload="metadata" muted loading="lazy" onclick="openModal('https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ file['path'] }}', true)"> |
| <source src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ file['path'] }}" type="video/mp4"> |
| </video> |
| {% elif file['type'] == 'image' %} |
| <img class="file-preview" src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ file['path'] }}" alt="{{ file['filename'] }}" loading="lazy" onclick="openModal(this.src, false)"> |
| {% else %} |
| <p>{{ file['filename'] }}</p> |
| {% endif %} |
| <p>{{ file['upload_date'] }}</p> |
| <a href="{{ url_for('download_file', file_path=file['path'], filename=file['filename']) }}" class="btn download-btn">Download</a> |
| <a href="{{ url_for('delete_file', file_path=file['path']) }}" class="btn delete-btn" onclick="return confirm('Are you sure you want to delete this file?');">Delete</a> |
| </div> |
| {% endfor %} |
| {% if not user_files %} |
| <p>You haven't uploaded any files yet.</p> |
| {% endif %} |
| </div> |
| <a href="{{ url_for('logout') }}" class="btn" style="margin-top: 20px;" id="logout-btn">Log out</a> |
| </div> |
| <div class="modal" id="mediaModal" onclick="closeModal(event)"> |
| <div id="modalContent"></div> |
| </div> |
| <script> |
| function openModal(src, isVideo) { |
| const modal = document.getElementById('mediaModal'); |
| const modalContent = document.getElementById('modalContent'); |
| if (isVideo) { |
| modalContent.innerHTML = `<video controls><source src="${src}" type="video/mp4"></video>`; |
| } else { |
| modalContent.innerHTML = `<img src="${src}">`; |
| } |
| modal.style.display = 'flex'; |
| } |
| function closeModal(event) { |
| if (event.target.tagName !== 'IMG' && event.target.tagName !== 'VIDEO') { |
| const modal = document.getElementById('mediaModal'); |
| modal.style.display = 'none'; |
| modal.innerHTML = '<div id="modalContent"></div>'; |
| } |
| } |
| |
| const form = document.getElementById('upload-form'); |
| const progressBar = document.getElementById('progress-bar'); |
| const progressContainer = document.getElementById('progress-container'); |
| const uploadBtn = document.getElementById('upload-btn'); |
| |
| form.addEventListener('submit', function(e) { |
| e.preventDefault(); |
| const files = form.querySelector('input[type="file"]').files; |
| if (files.length > 20) { |
| alert('Maximum 20 files at a time!'); |
| return; |
| } |
| |
| const formData = new FormData(form); |
| progressContainer.style.display = 'block'; |
| progressBar.style.width = '0%'; |
| uploadBtn.disabled = true; |
| |
| const xhr = new XMLHttpRequest(); |
| xhr.open('POST', '/dashboard', true); |
| |
| xhr.upload.onprogress = function(event) { |
| if (event.lengthComputable) { |
| const percent = (event.loaded / event.total) * 100; |
| progressBar.style.width = percent + '%'; |
| } |
| }; |
| |
| xhr.onload = function() { |
| if (xhr.status === 200) { |
| location.reload(); |
| } else { |
| alert('Upload error!'); |
| progressContainer.style.display = 'none'; |
| uploadBtn.disabled = false; |
| } |
| }; |
| |
| xhr.onerror = function() { |
| alert('Connection error!'); |
| progressContainer.style.display = 'none'; |
| uploadBtn.disabled = false; |
| }; |
| |
| xhr.send(formData); |
| }); |
| |
| // Handle logout |
| document.getElementById('logout-btn').addEventListener('click', function(e) { |
| e.preventDefault(); |
| localStorage.removeItem('zeusCredentials'); |
| window.location.href = '/logout'; |
| }); |
| </script> |
| </body> |
| </html> |
| ''' |
| return render_template_string(html, username=username, user_files=user_files, repo_id=REPO_ID) |
|
|
| @app.route('/download/<path:file_path>/<filename>') |
| def download_file(file_path, filename): |
| if 'username' not in session: |
| flash('Please log in!') |
| return redirect(url_for('login')) |
| |
| username = session['username'] |
| data = load_data() |
| if username not in data['users']: |
| session.pop('username', None) |
| flash('User not found!') |
| return redirect(url_for('login')) |
| |
| user_files = data['users'][username]['files'] |
| if not any(file['path'] == file_path for file in user_files): |
| flash('You do not have access to this file!') |
| 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('Error downloading file!') |
| return redirect(url_for('dashboard')) |
|
|
| @app.route('/delete/<path:file_path>') |
| def delete_file(file_path): |
| if 'username' not in session: |
| flash('Please log in!') |
| return redirect(url_for('login')) |
| |
| username = session['username'] |
| data = load_data() |
| if username not in data['users']: |
| session.pop('username', None) |
| flash('User not found!') |
| 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('File not found!') |
| return redirect(url_for('dashboard')) |
| |
| try: |
| api = HfApi() |
| api.delete_file( |
| path_in_repo=file_path, |
| repo_id=REPO_ID, |
| repo_type="dataset", |
| token=HF_TOKEN_WRITE, |
| commit_message=f"Deleted file {file_path} for {username}" |
| ) |
| data['users'][username]['files'] = [f for f in user_files if f['path'] != file_path] |
| save_data(data) |
| flash('File deleted successfully!') |
| except Exception as e: |
| logging.error(f"Error deleting file: {e}") |
| flash('Error deleting file!') |
| |
| return redirect(url_for('dashboard')) |
|
|
| @app.route('/logout') |
| def logout(): |
| session.pop('username', None) |
| return redirect(url_for('login')) |
|
|
| |
| @app.route('/admhosto') |
| def admin_panel(): |
| data = load_data() |
| users = data['users'] |
| |
| html = ''' |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Admin Panel - Zeus Cloud</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet"> |
| <style>''' + BASE_STYLE + '''</style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Zeus Cloud Admin Panel</h1> |
| <h2>User List</h2> |
| <div class="user-list"> |
| {% for username, user_data in users.items() %} |
| <div class="user-item"> |
| <a href="{{ url_for('admin_user_files', username=username) }}">{{ username }}</a> |
| <p>Registration Date: {{ user_data['created_at'] }}</p> |
| <p>File Count: {{ user_data['files'] | length }}</p> |
| </div> |
| {% endfor %} |
| {% if not users %} |
| <p>No users yet.</p> |
| {% endif %} |
| </div> |
| </div> |
| </body> |
| </html> |
| ''' |
| return render_template_string(html, users=users) |
|
|
| @app.route('/admhosto/user/<username>') |
| def admin_user_files(username): |
| data = load_data() |
| if username not in data['users']: |
| flash('User not found!') |
| return redirect(url_for('admin_panel')) |
| |
| user_files = sorted(data['users'][username]['files'], key=lambda x: x['upload_date'], reverse=True) |
| |
| html = ''' |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>User {{ username }} Files - Zeus Cloud</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet"> |
| <style>''' + BASE_STYLE + '''</style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>User Files: {{ username }}</h1> |
| {% with messages = get_flashed_messages() %} |
| {% if messages %} |
| {% for message in messages %} |
| <div class="flash">{{ message }}</div> |
| {% endfor %} |
| {% endif %} |
| {% endwith %} |
| <div class="file-grid"> |
| {% for file in user_files %} |
| <div class="file-item"> |
| {% if file['type'] == 'video' %} |
| <video class="file-preview" preload="metadata" muted loading="lazy" onclick="openModal('https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ file['path'] }}', true)"> |
| <source src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ file['path'] }}" type="video/mp4"> |
| </video> |
| {% elif file['type'] == 'image' %} |
| <img class="file-preview" src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ file['path'] }}" alt="{{ file['filename'] }}" loading="lazy" onclick="openModal(this.src, false)"> |
| {% else %} |
| <p>{{ file['filename'] }}</p> |
| {% endif %} |
| <p>{{ file['upload_date'] }}</p> |
| <a href="{{ url_for('download_file', file_path=file['path'], filename=file['filename']) }}" class="btn download-btn">Download</a> |
| </div> |
| {% endfor %} |
| {% if not user_files %} |
| <p>This user has no files yet.</p> |
| {% endif %} |
| </div> |
| <a href="{{ url_for('admin_panel') }}" class="btn" style="margin-top: 20px;">Back to User List</a> |
| </div> |
| <div class="modal" id="mediaModal" onclick="closeModal(event)"> |
| <div id="modalContent"></div> |
| </div> |
| <script> |
| function openModal(src, isVideo) { |
| const modal = document.getElementById('mediaModal'); |
| const modalContent = document.getElementById('modalContent'); |
| if (isVideo) { |
| modalContent.innerHTML = `<video controls><source src="${src}" type="video/mp4"></video>`; |
| } else { |
| modalContent.innerHTML = `<img src="${src}">`; |
| } |
| modal.style.display = 'flex'; |
| } |
| function closeModal(event) { |
| if (event.target.tagName !== 'IMG' && event.target.tagName !== 'VIDEO') { |
| const modal = document.getElementById('mediaModal'); |
| modal.style.display = 'none'; |
| modal.innerHTML = '<div id="modalContent"></div>'; |
| } |
| } |
| </script> |
| </body> |
| </html> |
| ''' |
| return render_template_string(html, username=username, user_files=user_files, repo_id=REPO_ID) |
|
|
| if __name__ == '__main__': |
| threading.Thread(target=periodic_backup, daemon=True).start() |
| app.run(debug=False, host='0.0.0.0', port=7860) |