|
|
@@ -0,0 +1,321 @@
|
|
|
+// HokoriTemp - JavaScript principal
|
|
|
+
|
|
|
+let selectedFile = null;
|
|
|
+// Usar configuración del servidor si está disponible, sino usar valor por defecto
|
|
|
+const MAX_FILE_SIZE = (typeof SERVER_CONFIG !== 'undefined' && SERVER_CONFIG.maxFileSize)
|
|
|
+ ? SERVER_CONFIG.maxFileSize
|
|
|
+ : 16 * 1024 * 1024; // 16MB por defecto
|
|
|
+
|
|
|
+// DOM Elements
|
|
|
+const dropZone = document.getElementById('dropZone');
|
|
|
+const fileInput = document.getElementById('fileInput');
|
|
|
+const fileInfo = document.getElementById('fileInfo');
|
|
|
+const fileName = document.getElementById('fileName');
|
|
|
+const fileSize = document.getElementById('fileSize');
|
|
|
+const removeFile = document.getElementById('removeFile');
|
|
|
+const uploadForm = document.getElementById('uploadForm');
|
|
|
+const uploadBtn = document.getElementById('uploadBtn');
|
|
|
+const uploadBtnText = document.getElementById('uploadBtnText');
|
|
|
+const uploadBtnLoading = document.getElementById('uploadBtnLoading');
|
|
|
+const resultSection = document.getElementById('resultSection');
|
|
|
+const emptyResultSection = document.getElementById('emptyResultSection');
|
|
|
+const errorSection = document.getElementById('errorSection');
|
|
|
+const errorMessage = document.getElementById('errorMessage');
|
|
|
+const downloadLink = document.getElementById('downloadLink');
|
|
|
+
|
|
|
+// Event Listeners
|
|
|
+dropZone.addEventListener('click', () => fileInput.click());
|
|
|
+dropZone.addEventListener('dragover', handleDragOver);
|
|
|
+dropZone.addEventListener('dragleave', handleDragLeave);
|
|
|
+dropZone.addEventListener('drop', handleDrop);
|
|
|
+fileInput.addEventListener('change', handleFileSelect);
|
|
|
+removeFile.addEventListener('click', removeSelectedFile);
|
|
|
+uploadForm.addEventListener('submit', handleUpload);
|
|
|
+
|
|
|
+// Inicializar estado
|
|
|
+document.addEventListener('DOMContentLoaded', function() {
|
|
|
+ // Mostrar el estado vacío del resultado al cargar
|
|
|
+ emptyResultSection.classList.remove('hidden');
|
|
|
+ emptyResultSection.classList.add('fade-in');
|
|
|
+});
|
|
|
+
|
|
|
+function handleDragOver(e) {
|
|
|
+ e.preventDefault();
|
|
|
+ dropZone.classList.add('dragover');
|
|
|
+}
|
|
|
+
|
|
|
+function handleDragLeave(e) {
|
|
|
+ e.preventDefault();
|
|
|
+ dropZone.classList.remove('dragover');
|
|
|
+}
|
|
|
+
|
|
|
+function handleDrop(e) {
|
|
|
+ e.preventDefault();
|
|
|
+ dropZone.classList.remove('dragover');
|
|
|
+ const files = e.dataTransfer.files;
|
|
|
+ if (files.length > 0) {
|
|
|
+ handleFile(files[0]);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleFileSelect(e) {
|
|
|
+ const file = e.target.files[0];
|
|
|
+ if (file) {
|
|
|
+ handleFile(file);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleFile(file) {
|
|
|
+ // Validar tamaño del archivo
|
|
|
+ if (file.size > MAX_FILE_SIZE) {
|
|
|
+ showError(`El archivo es demasiado grande. Tamaño máximo: ${getMaxFileSizeMB()}MB`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ selectedFile = file;
|
|
|
+ fileName.textContent = file.name;
|
|
|
+ fileSize.textContent = formatFileSize(file.size);
|
|
|
+
|
|
|
+ // Aplicar clase de advertencia si el archivo es grande (>10MB)
|
|
|
+ if (file.size > 10 * 1024 * 1024) {
|
|
|
+ fileInfo.classList.add('file-size-warning');
|
|
|
+ } else {
|
|
|
+ fileInfo.classList.remove('file-size-warning');
|
|
|
+ }
|
|
|
+
|
|
|
+ fileInfo.classList.remove('hidden');
|
|
|
+ uploadBtn.disabled = false;
|
|
|
+ hideError();
|
|
|
+}
|
|
|
+
|
|
|
+function removeSelectedFile() {
|
|
|
+ selectedFile = null;
|
|
|
+ fileInput.value = '';
|
|
|
+ fileInfo.classList.add('hidden');
|
|
|
+ fileInfo.classList.remove('file-size-warning', 'file-size-error');
|
|
|
+ uploadBtn.disabled = true;
|
|
|
+ // Ocultar resultado si hay uno mostrado
|
|
|
+ hideResult();
|
|
|
+}
|
|
|
+
|
|
|
+function formatFileSize(bytes) {
|
|
|
+ if (bytes === 0) return '0 Bytes';
|
|
|
+ const k = 1024;
|
|
|
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
|
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
|
+}
|
|
|
+
|
|
|
+async function handleUpload(e) {
|
|
|
+ e.preventDefault();
|
|
|
+
|
|
|
+ if (!selectedFile) {
|
|
|
+ showError('Por favor selecciona un archivo');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Validar tamaño del archivo antes de subir
|
|
|
+ if (selectedFile.size > MAX_FILE_SIZE) {
|
|
|
+ showError(`El archivo es demasiado grande. Tamaño máximo: ${getMaxFileSizeMB()}MB`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Show loading state
|
|
|
+ uploadBtn.disabled = true;
|
|
|
+ uploadBtnText.classList.add('hidden');
|
|
|
+ uploadBtnLoading.classList.remove('hidden');
|
|
|
+ hideError();
|
|
|
+ hideResult();
|
|
|
+
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append('file', selectedFile);
|
|
|
+
|
|
|
+ try {
|
|
|
+ const response = await fetch('/upload', {
|
|
|
+ method: 'POST',
|
|
|
+ body: formData
|
|
|
+ });
|
|
|
+
|
|
|
+ // Verificar si la respuesta es JSON válido
|
|
|
+ const contentType = response.headers.get('content-type');
|
|
|
+ const isJson = contentType && contentType.includes('application/json');
|
|
|
+
|
|
|
+ // Manejar diferentes códigos de respuesta
|
|
|
+ if (response.status === 413) {
|
|
|
+ showError(`El archivo es demasiado grande. Tamaño máximo: ${getMaxFileSizeMB()}MB`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!response.ok) {
|
|
|
+ let errorMessage = `Error del servidor (${response.status})`;
|
|
|
+
|
|
|
+ if (isJson) {
|
|
|
+ try {
|
|
|
+ const errorData = await response.json();
|
|
|
+ errorMessage = errorData.error || errorMessage;
|
|
|
+ } catch (e) {
|
|
|
+ console.error('Error parsing JSON response:', e);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Si no es JSON, intentar leer como texto
|
|
|
+ try {
|
|
|
+ const errorText = await response.text();
|
|
|
+ console.error('Error response:', errorText);
|
|
|
+
|
|
|
+ // Detectar errores específicos basados en el contenido
|
|
|
+ if (errorText.includes('Request Entity Too Large') || errorText.includes('413')) {
|
|
|
+ errorMessage = `El archivo es demasiado grande. Tamaño máximo: ${getMaxFileSizeMB()}MB`;
|
|
|
+ } else if (errorText.includes('413')) {
|
|
|
+ errorMessage = `El archivo es demasiado grande. Tamaño máximo: ${getMaxFileSizeMB()}MB`;
|
|
|
+ } else {
|
|
|
+ errorMessage = `Error del servidor: ${response.statusText}`;
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('Error reading response text:', e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ showError(errorMessage);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isJson) {
|
|
|
+ const result = await response.json();
|
|
|
+
|
|
|
+ if (result.success) {
|
|
|
+ // Save to localStorage
|
|
|
+ saveLinkToStorage(result);
|
|
|
+ showResult(result.download_url);
|
|
|
+ showNotification('Archivo subido exitosamente', 'success');
|
|
|
+ } else {
|
|
|
+ showError(result.error || 'Error al subir el archivo');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ showError('Respuesta inesperada del servidor');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Upload error:', error);
|
|
|
+
|
|
|
+ // Determinar el tipo de error
|
|
|
+ if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {
|
|
|
+ showError('Error de conexión. Verifica tu conexión a internet e intenta de nuevo.');
|
|
|
+ } else if (error.name === 'TypeError' && error.message.includes('JSON')) {
|
|
|
+ showError('Error al procesar la respuesta del servidor.');
|
|
|
+ } else {
|
|
|
+ showError('Error inesperado. Intenta de nuevo.');
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ // Reset loading state
|
|
|
+ uploadBtn.disabled = false;
|
|
|
+ uploadBtnText.classList.remove('hidden');
|
|
|
+ uploadBtnLoading.classList.add('hidden');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function saveLinkToStorage(result) {
|
|
|
+ const links = JSON.parse(localStorage.getItem('tempFileLinks') || '[]');
|
|
|
+ const newLink = {
|
|
|
+ id: result.file_id,
|
|
|
+ url: result.download_url,
|
|
|
+ filename: result.original_filename,
|
|
|
+ expiresAt: result.expires_at,
|
|
|
+ createdAt: new Date().toISOString()
|
|
|
+ };
|
|
|
+ links.push(newLink);
|
|
|
+ localStorage.setItem('tempFileLinks', JSON.stringify(links));
|
|
|
+}
|
|
|
+
|
|
|
+function showResult(url) {
|
|
|
+ downloadLink.value = url;
|
|
|
+ resultSection.classList.remove('hidden');
|
|
|
+ resultSection.classList.add('fade-in');
|
|
|
+ emptyResultSection.classList.add('hidden');
|
|
|
+}
|
|
|
+
|
|
|
+function showError(message) {
|
|
|
+ errorMessage.textContent = message;
|
|
|
+ errorSection.classList.remove('hidden');
|
|
|
+ showNotification(message, 'error');
|
|
|
+}
|
|
|
+
|
|
|
+function hideError() {
|
|
|
+ errorSection.classList.add('hidden');
|
|
|
+}
|
|
|
+
|
|
|
+function hideResult() {
|
|
|
+ resultSection.classList.add('hidden');
|
|
|
+ resultSection.classList.remove('fade-in');
|
|
|
+ emptyResultSection.classList.remove('hidden');
|
|
|
+ emptyResultSection.classList.add('fade-in');
|
|
|
+}
|
|
|
+
|
|
|
+function copyLink() {
|
|
|
+ downloadLink.select();
|
|
|
+ document.execCommand('copy');
|
|
|
+ // Show feedback
|
|
|
+ const copyBtn = event.target;
|
|
|
+ const originalText = copyBtn.textContent;
|
|
|
+ copyBtn.textContent = '¡Copiado!';
|
|
|
+ setTimeout(() => {
|
|
|
+ copyBtn.textContent = originalText;
|
|
|
+ }, 2000);
|
|
|
+ showNotification('Enlace copiado al portapapeles', 'success');
|
|
|
+}
|
|
|
+
|
|
|
+function openLink() {
|
|
|
+ window.open(downloadLink.value, '_blank');
|
|
|
+}
|
|
|
+
|
|
|
+function resetForm() {
|
|
|
+ removeSelectedFile();
|
|
|
+ hideResult();
|
|
|
+ hideError();
|
|
|
+ // Mostrar el estado vacío del resultado
|
|
|
+ emptyResultSection.classList.remove('hidden');
|
|
|
+}
|
|
|
+
|
|
|
+// Función para mostrar notificaciones
|
|
|
+function showNotification(message, type = 'info') {
|
|
|
+ // Crear elemento de notificación
|
|
|
+ const notification = document.createElement('div');
|
|
|
+ notification.className = `notification ${type}`;
|
|
|
+ notification.textContent = message;
|
|
|
+
|
|
|
+ // Agregar al DOM
|
|
|
+ document.body.appendChild(notification);
|
|
|
+
|
|
|
+ // Mostrar con animación
|
|
|
+ setTimeout(() => {
|
|
|
+ notification.classList.add('show');
|
|
|
+ }, 100);
|
|
|
+
|
|
|
+ // Ocultar después de 3 segundos
|
|
|
+ setTimeout(() => {
|
|
|
+ notification.classList.remove('show');
|
|
|
+ setTimeout(() => {
|
|
|
+ document.body.removeChild(notification);
|
|
|
+ }, 300);
|
|
|
+ }, 3000);
|
|
|
+}
|
|
|
+
|
|
|
+// Función helper para obtener el tamaño máximo en MB
|
|
|
+function getMaxFileSizeMB() {
|
|
|
+ return (typeof SERVER_CONFIG !== 'undefined' && SERVER_CONFIG.maxFileSizeMB)
|
|
|
+ ? SERVER_CONFIG.maxFileSizeMB
|
|
|
+ : MAX_FILE_SIZE / (1024 * 1024);
|
|
|
+}
|
|
|
+
|
|
|
+// Función para validar archivo antes de subir
|
|
|
+function validateFile(file) {
|
|
|
+ const maxSize = MAX_FILE_SIZE;
|
|
|
+ const maxSizeMB = getMaxFileSizeMB();
|
|
|
+
|
|
|
+ if (file.size > maxSize) {
|
|
|
+ return {
|
|
|
+ valid: false,
|
|
|
+ message: `El archivo es demasiado grande. Tamaño máximo: ${maxSizeMB}MB`
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return { valid: true };
|
|
|
+}
|