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 # 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.") # 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('/view') def view(): """Página para ver enlaces guardados en localStorage""" return render_template('view.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 # 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 filename = secure_filename(file.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) # Crear enlace temporal (expira en 24 horas) # 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': filename, 'expires_at': (datetime.now() + timedelta(hours=24)).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/') def download_file(file_id): """Descargar archivo por ID""" # Buscar archivo por ID for filename in os.listdir(app.config['UPLOAD_FOLDER']): if filename.startswith(file_id): file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) return send_file(file_path, as_attachment=True) return "Archivo no encontrado", 404 @app.route('/api/links') def get_links(): """API para obtener enlaces guardados (simulado)""" # En una implementación real, esto vendría de una base de datos return jsonify([]) if __name__ == '__main__': args = parse_arguments() # Asegurar que existe la carpeta de uploads ensure_upload_folder() # 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 )