| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- import os
- import argparse
- import uuid
- import shutil
- from datetime import datetime, timedelta
- from flask import Flask, render_template, request, jsonify, send_file, redirect, url_for
- from werkzeug.utils import secure_filename
- from dotenv import load_dotenv
- import json
- from database import init_database, save_file, get_file_by_id, get_all_files, delete_file, hard_delete_file, increment_download_count, cleanup_expired_files, get_file_stats
- # Cargar variables de entorno desde .env
- load_dotenv()
- app = Flask(__name__)
- app.config['MAX_CONTENT_LENGTH'] = int(os.getenv('MAX_FILE_SIZE', 16 * 1024 * 1024)) # 16MB max file size
- app.config['UPLOAD_FOLDER'] = os.getenv('UPLOAD_FOLDER', 'uploads')
- app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'tu_clave_secreta_aqui')
- # Manejador de errores para archivos demasiado grandes
- @app.errorhandler(413)
- def too_large(e):
- return jsonify({
- 'error': f'El archivo es demasiado grande. Tamaño máximo: {app.config["MAX_CONTENT_LENGTH"] // (1024*1024)}MB'
- }), 413
- # Manejador de errores general
- @app.errorhandler(500)
- def internal_error(e):
- return jsonify({'error': 'Error interno del servidor'}), 500
- @app.errorhandler(404)
- def not_found(e):
- return jsonify({'error': 'Página no encontrada'}), 404
- # Verificar y crear carpeta de uploads si no existe
- def ensure_upload_folder():
- if not os.path.exists(app.config['UPLOAD_FOLDER']):
- os.makedirs(app.config['UPLOAD_FOLDER'])
- print(f"Carpeta '{app.config['UPLOAD_FOLDER']}' creada automáticamente.")
- # Inicializar base de datos
- def init_app():
- ensure_upload_folder()
- init_database()
- print("Base de datos inicializada correctamente.")
- # Configurar argumentos de línea de comandos
- def parse_arguments():
- parser = argparse.ArgumentParser(description='Servidor de archivos temporales')
- parser.add_argument('--host', default=os.getenv('HOST', '127.0.0.1'), help='Host del servidor (default: 127.0.0.1)')
- parser.add_argument('--port', type=int, default=int(os.getenv('PORT', 5000)), help='Puerto del servidor (default: 5000)')
- parser.add_argument('--https', action='store_true', help='Usar HTTPS')
- parser.add_argument('--debug', action='store_true', default=os.getenv('DEBUG', 'False').lower() == 'true', help='Modo debug')
- return parser.parse_args()
- @app.route('/')
- def index():
- """Página principal para subir archivos"""
- # Pasar configuración al template
- max_file_size = app.config['MAX_CONTENT_LENGTH']
- max_file_size_mb = max_file_size // (1024 * 1024)
-
- return render_template('index.html',
- max_file_size=max_file_size,
- max_file_size_mb=max_file_size_mb)
- @app.route('/myfiles')
- def myfiles():
- """Página para ver enlaces guardados en localStorage"""
- return render_template('myfiles.html')
- @app.route('/upload', methods=['POST'])
- def upload_file():
- """Manejar la subida de archivos"""
- try:
- # Verificar si hay archivo en la request
- if 'file' not in request.files:
- return jsonify({'error': 'No se seleccionó ningún archivo'}), 400
-
- file = request.files['file']
- if file.filename == '':
- return jsonify({'error': 'No se seleccionó ningún archivo'}), 400
-
- # Obtener tiempo de vida del archivo (por defecto 24 horas)
- expires_hours = request.form.get('expires_hours', 24, type=int)
-
- # Validar tiempo de vida (entre 1 hora y 7 días)
- if expires_hours < 1 or expires_hours > 168: # 168 horas = 7 días
- expires_hours = 24
-
- # Validar tamaño del archivo antes de procesar
- max_size = app.config['MAX_CONTENT_LENGTH']
- max_size_mb = max_size // (1024 * 1024)
-
- # Verificar content_length si está disponible
- if request.content_length and request.content_length > max_size:
- return jsonify({
- 'error': f'El archivo es demasiado grande. Tamaño máximo: {max_size_mb}MB'
- }), 413
-
- # Verificar el tamaño del archivo después de recibirlo
- if file:
- # Leer el archivo en chunks para verificar el tamaño
- file.seek(0, 2) # Ir al final del archivo
- file_size = file.tell()
- file.seek(0) # Volver al inicio
-
- if file_size > max_size:
- return jsonify({
- 'error': f'El archivo es demasiado grande. Tamaño máximo: {max_size_mb}MB'
- }), 413
-
- # Generar nombre único para el archivo
- original_filename = file.filename
- if not original_filename:
- return jsonify({'error': 'Nombre de archivo no válido'}), 400
-
- filename = secure_filename(original_filename)
- if not filename:
- return jsonify({'error': 'Nombre de archivo no válido'}), 400
-
- file_id = str(uuid.uuid4())
- file_extension = os.path.splitext(filename)[1]
- unique_filename = f"{file_id}{file_extension}"
-
- # Guardar archivo
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
- file.save(file_path)
-
- # Guardar en base de datos con el tiempo de vida especificado
- save_file(file_id, original_filename, unique_filename, file_size, expires_hours)
-
- # Crear enlace temporal con el tiempo de vida especificado
- # Usar el dominio configurado o el host actual
- domain = os.getenv('DOMAIN', request.host)
- protocol = 'https' if args.https else 'http'
- download_url = f"{protocol}://{domain}/download/{file_id}"
-
- return jsonify({
- 'success': True,
- 'download_url': download_url,
- 'file_id': file_id,
- 'original_filename': original_filename,
- 'expires_at': (datetime.now() + timedelta(hours=expires_hours)).isoformat()
- })
- else:
- return jsonify({'error': 'No se pudo procesar el archivo'}), 400
-
- except Exception as e:
- print(f"Error en upload: {str(e)}")
- return jsonify({'error': 'Error interno del servidor'}), 500
- @app.route('/download/<file_id>')
- def download_file(file_id):
- """Descargar archivo por ID"""
- # Buscar archivo en la base de datos
- file_info = get_file_by_id(file_id)
- if not file_info:
- return "Archivo no encontrado", 404
-
- # Verificar si el archivo ha expirado
- expires_at = datetime.fromisoformat(file_info['expires_at'])
- if expires_at <= datetime.now():
- return "Archivo expirado", 410
-
- # Verificar si el archivo existe físicamente
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], file_info['stored_filename'])
- if not os.path.exists(file_path):
- return "Archivo no encontrado", 404
-
- # Incrementar contador de descargas
- increment_download_count(file_id)
-
- # Enviar archivo
- return send_file(file_path, as_attachment=True, download_name=file_info['original_filename'])
- @app.route('/api/links')
- def get_links():
- """API para obtener enlaces guardados desde la base de datos"""
- try:
- files = get_all_files()
- links = []
-
- for file_info in files:
- # Crear URL de descarga
- domain = os.getenv('DOMAIN', request.host)
- protocol = 'https' if args.https else 'http'
- download_url = f"{protocol}://{domain}/download/{file_info['id']}"
-
- links.append({
- 'id': file_info['id'],
- 'filename': file_info['original_filename'],
- 'url': download_url,
- 'created_at': file_info['created_at'],
- 'expires_at': file_info['expires_at'],
- 'file_size': file_info['file_size'],
- 'download_count': file_info['download_count']
- })
-
- return jsonify(links)
- except Exception as e:
- print(f"Error obteniendo enlaces: {str(e)}")
- return jsonify({'error': 'Error interno del servidor'}), 500
- @app.route('/api/delete/<file_id>', methods=['DELETE'])
- def delete_link(file_id):
- """API para eliminar un enlace permanentemente"""
- try:
- # Obtener información del archivo
- file_info = get_file_by_id(file_id)
- if not file_info:
- return jsonify({'error': 'Archivo no encontrado'}), 404
-
- # Eliminar archivo físico
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], file_info['stored_filename'])
- if os.path.exists(file_path):
- os.remove(file_path)
-
- # Eliminar permanentemente de la base de datos
- hard_delete_file(file_id)
-
- return jsonify({'success': True, 'message': 'Archivo eliminado permanentemente'})
- except Exception as e:
- print(f"Error eliminando archivo: {str(e)}")
- return jsonify({'error': 'Error interno del servidor'}), 500
- @app.route('/api/stats')
- def get_stats():
- """API para obtener estadísticas"""
- try:
- stats = get_file_stats()
- return jsonify(stats)
- except Exception as e:
- print(f"Error obteniendo estadísticas: {str(e)}")
- return jsonify({'error': 'Error interno del servidor'}), 500
- if __name__ == '__main__':
- args = parse_arguments()
-
- # Inicializar aplicación (carpeta de uploads y base de datos)
- init_app()
-
- # Configurar protocolo
- protocol = 'https' if args.https else 'http'
-
- print(f"Servidor iniciando en {protocol}://{args.host}:{args.port}")
- print(f"Modo debug: {'Activado' if args.debug else 'Desactivado'}")
-
- app.run(
- host=args.host,
- port=args.port,
- debug=args.debug
- )
|