lea97338 commited on
Commit
414516d
·
verified ·
1 Parent(s): 9fc2a8d

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +438 -0
app.py ADDED
@@ -0,0 +1,438 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ import os
4
+ import uuid
5
+ import json
6
+ import threading
7
+ from datetime import datetime
8
+ from flask import Flask, request, jsonify, render_template, send_from_directory, redirect, url_for, session
9
+ from werkzeug.utils import secure_filename
10
+ from werkzeug.exceptions import RequestEntityTooLarge
11
+ from PIL import Image
12
+ import hashlib
13
+
14
+ app = Flask(__name__)
15
+ app.config['MAX_CONTENT_LENGTH'] = 8 * 1024 * 1024 * 1024 # 8GB
16
+ app.config['UPLOAD_FOLDER'] = 'uploads'
17
+ app.config['THUMBNAILS_FOLDER'] = 'thumbnails'
18
+ app.secret_key = 'votre-cle-secrete-super-secure'
19
+
20
+ # Fichier pour stocker les utilisateurs
21
+ USERS_FILE = 'users.json'
22
+
23
+ # Utilisateurs par défaut
24
+ DEFAULT_USERS = {
25
+ 'admin': {
26
+ 'password': hashlib.sha256('admin123'.encode()).hexdigest(),
27
+ 'role': 'admin'
28
+ },
29
+ 'user1': {
30
+ 'password': hashlib.sha256('password1'.encode()).hexdigest(),
31
+ 'role': 'user'
32
+ },
33
+ 'user2': {
34
+ 'password': hashlib.sha256('password2'.encode()).hexdigest(),
35
+ 'role': 'user'
36
+ },
37
+ 'demo': {
38
+ 'password': hashlib.sha256('demo'.encode()).hexdigest(),
39
+ 'role': 'user'
40
+ }
41
+ }
42
+
43
+ def load_users():
44
+ """Charger les utilisateurs depuis le fichier JSON"""
45
+ if os.path.exists(USERS_FILE):
46
+ try:
47
+ with open(USERS_FILE, 'r') as f:
48
+ return json.load(f)
49
+ except:
50
+ pass
51
+ return DEFAULT_USERS.copy()
52
+
53
+ def save_users(users):
54
+ """Sauvegarder les utilisateurs dans le fichier JSON"""
55
+ with open(USERS_FILE, 'w') as f:
56
+ json.dump(users, f, indent=2)
57
+
58
+ # Dictionnaire pour stocker les progrès d'upload
59
+ upload_progress = {}
60
+ upload_lock = threading.Lock()
61
+
62
+ # Types de fichiers autorisés
63
+ ALLOWED_EXTENSIONS = {
64
+ 'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'tiff',
65
+ 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'rar', '7z',
66
+ 'mp3', 'wav', 'flac', 'mp4', 'avi', 'mov', 'mkv', 'webm', 'wmv',
67
+ 'py', 'js', 'html', 'css', 'json', 'xml', 'csv','heic'
68
+ }
69
+
70
+ # Types de fichiers pour aperçu
71
+ IMAGE_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'tiff'}
72
+ VIDEO_EXTENSIONS = {'mp4', 'avi', 'mov', 'mkv', 'webm', 'wmv'}
73
+
74
+ def allowed_file(filename):
75
+ return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
76
+
77
+ def get_file_size(filepath):
78
+ try:
79
+ return os.path.getsize(filepath)
80
+ except:
81
+ return 0
82
+
83
+ def format_file_size(size_bytes):
84
+ if size_bytes == 0:
85
+ return "0 B"
86
+ size_names = ["B", "KB", "MB", "GB"]
87
+ i = 0
88
+ while size_bytes >= 1024 and i < len(size_names) - 1:
89
+ size_bytes /= 1024.0
90
+ i += 1
91
+ return f"{size_bytes:.1f} {size_names[i]}"
92
+
93
+ def is_image(filename):
94
+ ext = filename.rsplit('.', 1)[1].lower() if '.' in filename else ''
95
+ return ext in IMAGE_EXTENSIONS
96
+
97
+ def is_video(filename):
98
+ ext = filename.rsplit('.', 1)[1].lower() if '.' in filename else ''
99
+ return ext in VIDEO_EXTENSIONS
100
+
101
+ def create_thumbnail(filepath, filename):
102
+ """Créer une vignette pour les images"""
103
+ try:
104
+ if not is_image(filename):
105
+ return None
106
+
107
+ thumbnails_dir = app.config['THUMBNAILS_FOLDER']
108
+ os.makedirs(thumbnails_dir, exist_ok=True)
109
+
110
+ # Nom de la vignette
111
+ name, ext = os.path.splitext(filename)
112
+ thumbnail_name = f"{name}_thumb.jpg"
113
+ thumbnail_path = os.path.join(thumbnails_dir, thumbnail_name)
114
+
115
+ # Créer la vignette
116
+ with Image.open(filepath) as img:
117
+ # Convertir en RGB si nécessaire
118
+ if img.mode in ('RGBA', 'LA', 'P'):
119
+ img = img.convert('RGB')
120
+
121
+ # Redimensionner en gardant les proportions
122
+ img.thumbnail((200, 200), Image.Resampling.LANCZOS)
123
+ img.save(thumbnail_path, 'JPEG', quality=85)
124
+
125
+ return thumbnail_name
126
+ except Exception as e:
127
+ print(f"Erreur création vignette: {e}")
128
+ return None
129
+
130
+ def get_user_folder(username):
131
+ """Obtenir le dossier utilisateur"""
132
+ user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username)
133
+ os.makedirs(user_folder, exist_ok=True)
134
+ return user_folder
135
+
136
+ def require_auth(f):
137
+ """Décorateur pour vérifier l'authentification"""
138
+ def decorated_function(*args, **kwargs):
139
+ if 'username' not in session:
140
+ if request.is_json or request.headers.get('Content-Type') == 'application/json':
141
+ return jsonify({'error': 'Non authentifié', 'redirect': '/login'}), 401
142
+ return redirect('/login')
143
+ return f(*args, **kwargs)
144
+ decorated_function.__name__ = f.__name__
145
+ return decorated_function
146
+
147
+ def require_admin(f):
148
+ """Décorateur pour vérifier les droits admin"""
149
+ def decorated_function(*args, **kwargs):
150
+ if 'username' not in session:
151
+ return redirect('/login')
152
+
153
+ users = load_users()
154
+ user_info = users.get(session['username'], {})
155
+ if user_info.get('role') != 'admin':
156
+ return jsonify({'error': 'Accès refusé - Droits administrateur requis'}), 403
157
+
158
+ return f(*args, **kwargs)
159
+ decorated_function.__name__ = f.__name__
160
+ return decorated_function
161
+
162
+ @app.before_request
163
+ def check_auth():
164
+ """Vérifier l'authentification pour toutes les routes sauf login"""
165
+ public_routes = ['/login', '/static']
166
+
167
+ is_public = any(request.path.startswith(route) for route in public_routes)
168
+
169
+ if not is_public and 'username' not in session:
170
+ if request.is_json or request.headers.get('Content-Type') == 'application/json':
171
+ return jsonify({'error': 'Non authentifié', 'redirect': '/login'}), 401
172
+ return redirect('/login')
173
+
174
+ @app.route('/')
175
+ def index():
176
+ if 'username' not in session:
177
+ return redirect('/login')
178
+
179
+ users = load_users()
180
+ user_info = users.get(session['username'], {})
181
+ is_admin = user_info.get('role') == 'admin'
182
+
183
+ return render_template('index.html', username=session['username'], is_admin=is_admin)
184
+
185
+ @app.route('/login')
186
+ def login_page():
187
+ if 'username' in session:
188
+ return redirect('/')
189
+ return render_template('login.html')
190
+
191
+ @app.route('/login', methods=['POST'])
192
+ def login():
193
+ data = request.get_json()
194
+ username = data.get('username', '').strip()
195
+ password = data.get('password', '').strip()
196
+
197
+ if not username or not password:
198
+ return jsonify({'success': False, 'error': 'Identifiant et mot de passe requis'})
199
+
200
+ users = load_users()
201
+ password_hash = hashlib.sha256(password.encode()).hexdigest()
202
+
203
+ if username in users and users[username]['password'] == password_hash:
204
+ session['username'] = username
205
+ return jsonify({'success': True, 'redirect': '/'})
206
+ else:
207
+ return jsonify({'success': False, 'error': 'Identifiants incorrects'})
208
+
209
+ @app.route('/logout')
210
+ def logout():
211
+ session.pop('username', None)
212
+ return redirect('/login')
213
+
214
+ @app.route('/api/files')
215
+ @require_auth
216
+ def api_files():
217
+ files = []
218
+ user_folder = get_user_folder(session['username'])
219
+
220
+ if os.path.exists(user_folder):
221
+ for filename in os.listdir(user_folder):
222
+ filepath = os.path.join(user_folder, filename)
223
+ if os.path.isfile(filepath):
224
+ file_size = get_file_size(filepath)
225
+
226
+ file_info = {
227
+ 'name': filename,
228
+ 'size': format_file_size(file_size),
229
+ 'size_bytes': file_size,
230
+ 'date': datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %H:%M:%S'),
231
+ 'is_image': is_image(filename),
232
+ 'is_video': is_video(filename),
233
+ 'thumbnail': None
234
+ }
235
+
236
+ if is_image(filename):
237
+ thumbnail_name = create_thumbnail(filepath, filename)
238
+ if thumbnail_name:
239
+ file_info['thumbnail'] = thumbnail_name
240
+
241
+ files.append(file_info)
242
+
243
+ files.sort(key=lambda x: x['date'], reverse=True)
244
+ total_size = sum(f['size_bytes'] for f in files)
245
+
246
+ return jsonify({
247
+ 'files': files,
248
+ 'total_files': len(files),
249
+ 'total_size': format_file_size(total_size)
250
+ })
251
+
252
+ @app.route('/upload', methods=['POST'])
253
+ @require_auth
254
+ def upload_file():
255
+ try:
256
+ if 'file' not in request.files:
257
+ return jsonify({'error': 'Aucun fichier sélectionné'}), 400
258
+
259
+ file = request.files['file']
260
+ if file.filename == '':
261
+ return jsonify({'error': 'Aucun fichier sélectionné'}), 400
262
+
263
+ if not allowed_file(file.filename):
264
+ return jsonify({'error': 'Type de fichier non autorisé'}), 400
265
+
266
+ user_folder = get_user_folder(session['username'])
267
+ filename = secure_filename(file.filename)
268
+
269
+ base_name, ext = os.path.splitext(filename)
270
+ counter = 1
271
+ while os.path.exists(os.path.join(user_folder, filename)):
272
+ filename = f"{base_name}_{counter}{ext}"
273
+ counter += 1
274
+
275
+ filepath = os.path.join(user_folder, filename)
276
+ file.save(filepath)
277
+
278
+ thumbnail_name = None
279
+ if is_image(filename):
280
+ thumbnail_name = create_thumbnail(filepath, filename)
281
+
282
+ file_size = get_file_size(filepath)
283
+
284
+ return jsonify({
285
+ 'success': True,
286
+ 'message': f'Fichier {filename} uploadé avec succès!',
287
+ 'filename': filename,
288
+ 'size': format_file_size(file_size),
289
+ 'thumbnail': thumbnail_name
290
+ })
291
+
292
+ except RequestEntityTooLarge:
293
+ return jsonify({'error': 'Fichier trop volumineux (max 8GB)'}), 413
294
+ except Exception as e:
295
+ return jsonify({'error': f'Erreur lors de l\'upload: {str(e)}'}), 500
296
+
297
+ @app.route('/download/<filename>')
298
+ @require_auth
299
+ def download_file(filename):
300
+ try:
301
+ user_folder = get_user_folder(session['username'])
302
+ return send_from_directory(user_folder, filename, as_attachment=True)
303
+ except FileNotFoundError:
304
+ return jsonify({'error': 'Fichier non trouvé'}), 404
305
+
306
+ @app.route('/delete/<filename>', methods=['DELETE'])
307
+ @require_auth
308
+ def delete_file(filename):
309
+ try:
310
+ user_folder = get_user_folder(session['username'])
311
+ filepath = os.path.join(user_folder, filename)
312
+
313
+ if os.path.exists(filepath):
314
+ os.remove(filepath)
315
+
316
+ if is_image(filename):
317
+ name, ext = os.path.splitext(filename)
318
+ thumbnail_name = f"{name}_thumb.jpg"
319
+ thumbnail_path = os.path.join(app.config['THUMBNAILS_FOLDER'], thumbnail_name)
320
+ if os.path.exists(thumbnail_path):
321
+ os.remove(thumbnail_path)
322
+
323
+ return jsonify({'success': True, 'message': f'Fichier {filename} supprimé'})
324
+ else:
325
+ return jsonify({'error': 'Fichier non trouvé'}), 404
326
+ except Exception as e:
327
+ return jsonify({'error': f'Erreur lors de la suppression: {str(e)}'}), 500
328
+
329
+ @app.route('/thumbnail/<thumbnail_name>')
330
+ @require_auth
331
+ def get_thumbnail(thumbnail_name):
332
+ try:
333
+ return send_from_directory(app.config['THUMBNAILS_FOLDER'], thumbnail_name)
334
+ except FileNotFoundError:
335
+ return jsonify({'error': 'Vignette non trouvée'}), 404
336
+
337
+ @app.route('/preview/<filename>')
338
+ @require_auth
339
+ def preview_file(filename):
340
+ try:
341
+ user_folder = get_user_folder(session['username'])
342
+ return send_from_directory(user_folder, filename)
343
+ except FileNotFoundError:
344
+ return jsonify({'error': 'Fichier non trouvé'}), 404
345
+
346
+ # Routes d'administration
347
+ @app.route('/admin/users')
348
+ @require_admin
349
+ def admin_users():
350
+ users = load_users()
351
+ user_list = []
352
+ for username, info in users.items():
353
+ user_list.append({
354
+ 'username': username,
355
+ 'role': info.get('role', 'user')
356
+ })
357
+ return jsonify({'users': user_list})
358
+
359
+ @app.route('/admin/users', methods=['POST'])
360
+ @require_admin
361
+ def add_user():
362
+ data = request.get_json()
363
+ username = data.get('username', '').strip()
364
+ password = data.get('password', '').strip()
365
+ role = data.get('role', 'user')
366
+
367
+ if not username or not password:
368
+ return jsonify({'success': False, 'error': 'Nom d\'utilisateur et mot de passe requis'})
369
+
370
+ users = load_users()
371
+ if username in users:
372
+ return jsonify({'success': False, 'error': 'Utilisateur déjà existant'})
373
+
374
+ users[username] = {
375
+ 'password': hashlib.sha256(password.encode()).hexdigest(),
376
+ 'role': role
377
+ }
378
+
379
+ save_users(users)
380
+ return jsonify({'success': True, 'message': f'Utilisateur {username} créé avec succès'})
381
+
382
+ @app.route('/admin/users/<username>', methods=['DELETE'])
383
+ @require_admin
384
+ def delete_user(username):
385
+ if username == 'admin':
386
+ return jsonify({'success': False, 'error': 'Impossible de supprimer l\'administrateur principal'})
387
+
388
+ users = load_users()
389
+ if username not in users:
390
+ return jsonify({'success': False, 'error': 'Utilisateur non trouvé'})
391
+
392
+ # Supprimer les fichiers de l'utilisateur
393
+ user_folder = get_user_folder(username)
394
+ if os.path.exists(user_folder):
395
+ import shutil
396
+ shutil.rmtree(user_folder)
397
+
398
+ del users[username]
399
+ save_users(users)
400
+
401
+ return jsonify({'success': True, 'message': f'Utilisateur {username} supprimé avec succès'})
402
+
403
+ @app.errorhandler(413)
404
+ def too_large(e):
405
+ return jsonify({'error': 'Fichier trop volumineux (max 8GB)'}), 413
406
+
407
+ @app.errorhandler(404)
408
+ def not_found(e):
409
+ return jsonify({'error': 'Page non trouvée'}), 404
410
+
411
+ @app.errorhandler(500)
412
+ def server_error(e):
413
+ return jsonify({'error': 'Erreur serveur interne'}), 500
414
+
415
+ if __name__ == '__main__':
416
+ os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
417
+ os.makedirs(app.config['THUMBNAILS_FOLDER'], exist_ok=True)
418
+
419
+ print("🚀 Démarrage du serveur de gestion de fichiers...")
420
+ print("🌐 Accédez au site sur: http://0.0.0.0:7777")
421
+ print("📱 Interface optimisée pour mobile et desktop")
422
+ print("👥 Utilisateurs disponibles:")
423
+ print(" • admin / admin123 (Administrateur)")
424
+ print(" • user1 / password1")
425
+ print(" • user2 / password2")
426
+ print(" • demo / demo")
427
+ print("⚡ Fonctionnalités:")
428
+ print(" • Authentification multi-utilisateurs")
429
+ print(" • Gestion des utilisateurs (admin)")
430
+ print(" • Upload de fichiers (jusqu'à 8GB)")
431
+ print(" • Aperçu des images et vidéos")
432
+ print(" • Vignettes automatiques")
433
+ print(" • Download et suppression de fichiers")
434
+ print(" • Interface responsive")
435
+ print(" • API JSON disponible sur /api/files")
436
+ print("\n🛑 Appuyez sur Ctrl+C pour arrêter le serveur")
437
+
438
+ app.run(host='0.0.0.0', port=7777, debug=True, threaded=True)