AutoBackups/templates/config.html

887 lines
46 KiB
HTML

{% extends "base.html" %}
{% block title %}Configuración - AutoBackups{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<h1 class="mb-4">
<i class="bi bi-sliders"></i> Configuración del Sistema
</h1>
</div>
</div>
<div class="row">
<div class="col-12">
<!-- Alert container for messages -->
<div id="config-alerts"></div>
<!-- Configuration Tabs -->
<ul class="nav nav-tabs" id="configTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="general-tab" data-bs-toggle="tab"
data-bs-target="#general" type="button" role="tab">
<i class="bi bi-gear-fill"></i> General
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="directories-tab" data-bs-toggle="tab"
data-bs-target="#directories" type="button" role="tab">
<i class="bi bi-folder"></i> Directorios
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="backup-tab" data-bs-toggle="tab"
data-bs-target="#backup" type="button" role="tab">
<i class="bi bi-archive"></i> Backup
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="everything-tab" data-bs-toggle="tab"
data-bs-target="#everything" type="button" role="tab">
<i class="bi bi-search"></i> Everything API
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="web-tab" data-bs-toggle="tab"
data-bs-target="#web" type="button" role="tab">
<i class="bi bi-globe"></i> Web Interface
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="logging-tab" data-bs-toggle="tab"
data-bs-target="#logging" type="button" role="tab">
<i class="bi bi-file-text"></i> Logging
</button>
</li>
</ul>
<div class="tab-content mt-3" id="configTabContent">
<!-- General Settings Tab -->
<div class="tab-pane fade show active" id="general" role="tabpanel">
<div class="card">
<div class="card-header">
<h5><i class="bi bi-gear-fill"></i> Configuración General</h5>
</div>
<div class="card-body">
<form id="general-form">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="backup_destination" class="form-label">Destino de Backups</label>
<div class="input-group">
<input type="text" class="form-control" id="backup_destination"
value="{{ config.backup_destination }}" required>
<button type="button" class="btn btn-outline-secondary" id="browse-backup-dest">
<i class="bi bi-folder"></i>
</button>
</div>
<div class="form-text">Directorio donde se almacenarán los backups</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="default_schedule" class="form-label">Programación por Defecto</label>
<select class="form-select" id="default_schedule">
<option value="manual" {% if config.global_settings.default_schedule == 'manual' %}selected{% endif %}>Manual</option>
<option value="daily" {% if config.global_settings.default_schedule == 'daily' %}selected{% endif %}>Diario</option>
<option value="weekly" {% if config.global_settings.default_schedule == 'weekly' %}selected{% endif %}>Semanal</option>
<option value="monthly" {% if config.global_settings.default_schedule == 'monthly' %}selected{% endif %}>Mensual</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="default_schedule_time" class="form-label">Hora de Programación</label>
<input type="time" class="form-control" id="default_schedule_time"
value="{{ config.global_settings.default_schedule_time }}">
<div class="form-text">Hora por defecto para ejecutar backups automáticos</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="scan_interval_minutes" class="form-label">Intervalo de Escaneo (minutos)</label>
<input type="number" class="form-control" id="scan_interval_minutes"
value="{{ config.global_settings.scan_interval_minutes }}" min="1" max="1440">
<div class="form-text">Frecuencia de búsqueda de nuevos proyectos</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="min_backup_interval_minutes" class="form-label">Intervalo Mínimo de Backup (minutos)</label>
<input type="number" class="form-control" id="min_backup_interval_minutes"
value="{{ config.global_settings.min_backup_interval_minutes }}" min="1">
<div class="form-text">Tiempo mínimo entre backups del mismo proyecto</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="min_free_space_mb" class="form-label">Espacio Libre Mínimo (MB)</label>
<input type="number" class="form-control" id="min_free_space_mb"
value="{{ config.global_settings.min_free_space_mb }}" min="50">
<div class="form-text">Espacio mínimo requerido antes de hacer backup</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="retry_delay_hours" class="form-label">Retraso de Reintento (horas)</label>
<input type="number" class="form-control" id="retry_delay_hours"
value="{{ config.global_settings.retry_delay_hours }}" min="0" step="0.5">
<div class="form-text">Tiempo de espera antes de reintentar un backup fallido</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="backup_timeout_minutes" class="form-label">Timeout de Backup (minutos)</label>
<input type="number" class="form-control" id="backup_timeout_minutes"
value="{{ config.global_settings.backup_timeout_minutes }}" min="0">
<div class="form-text">Tiempo máximo para completar un backup (0 = sin límite)</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary">
<i class="bi bi-check-circle"></i> Guardar Configuración General
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Observation Directories Tab -->
<div class="tab-pane fade" id="directories" role="tabpanel">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="bi bi-folder"></i> Directorios de Observación</h5>
<button type="button" class="btn btn-success btn-sm" data-bs-toggle="modal" data-bs-target="#addDirectoryModal">
<i class="bi bi-plus-circle"></i> Agregar Directorio
</button>
</div>
<div class="card-body">
<div id="directories-list">
{% for dir in config.observation_directories %}
<div class="card mb-2 directory-item" data-index="{{ loop.index0 }}">
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-4">
<strong>{{ dir.path }}</strong>
</div>
<div class="col-md-2">
<span class="badge bg-info">{{ dir.type }}</span>
</div>
<div class="col-md-4">
<small class="text-muted">{{ dir.description or 'Sin descripción' }}</small>
</div>
<div class="col-md-2 text-end">
<div class="form-check form-switch d-inline-block me-2">
<input class="form-check-input" type="checkbox"
{% if dir.enabled %}checked{% endif %}
onchange="toggleDirectory({{ loop.index0 }}, this.checked)">
</div>
<button type="button" class="btn btn-danger btn-sm"
onclick="removeDirectory({{ loop.index0 }})">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<!-- Backup Options Tab -->
<div class="tab-pane fade" id="backup" role="tabpanel">
<div class="card">
<div class="card-header">
<h5><i class="bi bi-archive"></i> Opciones de Backup</h5>
</div>
<div class="card-body">
<form id="backup-form">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="compression_level" class="form-label">Nivel de Compresión</label>
<input type="range" class="form-range" id="compression_level"
min="0" max="9" value="{{ config.backup_options.compression_level }}"
oninput="document.getElementById('compression_value').textContent = this.value">
<div class="d-flex justify-content-between">
<small>Rápido (0)</small>
<span id="compression_value">{{ config.backup_options.compression_level }}</span>
<small>Máximo (9)</small>
</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="hash_algorithm" class="form-label">Algoritmo de Hash</label>
<select class="form-select" id="hash_algorithm">
<option value="md5" {% if config.backup_options.hash_algorithm == 'md5' %}selected{% endif %}>MD5</option>
<option value="sha1" {% if config.backup_options.hash_algorithm == 'sha1' %}selected{% endif %}>SHA1</option>
<option value="sha256" {% if config.backup_options.hash_algorithm == 'sha256' %}selected{% endif %}>SHA256</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="process_priority" class="form-label">Prioridad del Proceso</label>
<select class="form-select" id="process_priority">
<option value="low" {% if config.backup_options.process_priority == 'low' %}selected{% endif %}>Baja</option>
<option value="normal" {% if config.backup_options.process_priority == 'normal' %}selected{% endif %}>Normal</option>
<option value="high" {% if config.backup_options.process_priority == 'high' %}selected{% endif %}>Alta</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="backup_type" class="form-label">Tipo de Backup</label>
<select class="form-select" id="backup_type">
<option value="complete" {% if config.backup_options.backup_type == 'complete' %}selected{% endif %}>Completo</option>
<option value="incremental" {% if config.backup_options.backup_type == 'incremental' %}selected{% endif %}>Incremental</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="filename_format" class="form-label">Formato de Nombre de Archivo</label>
<input type="text" class="form-control" id="filename_format"
value="{{ config.backup_options.filename_format }}">
<div class="form-text">Formato: HH-MM-SS_projects.zip</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="date_format" class="form-label">Formato de Fecha</label>
<input type="text" class="form-control" id="date_format"
value="{{ config.backup_options.date_format }}">
<div class="form-text">Formato: YYYY-MM-DD</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="include_subdirectories"
{% if config.backup_options.include_subdirectories %}checked{% endif %}>
<label class="form-check-label" for="include_subdirectories">
Incluir Subdirectorios
</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="preserve_directory_structure"
{% if config.backup_options.preserve_directory_structure %}checked{% endif %}>
<label class="form-check-label" for="preserve_directory_structure">
Preservar Estructura de Directorios
</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sequential_execution"
{% if config.backup_options.sequential_execution %}checked{% endif %}>
<label class="form-check-label" for="sequential_execution">
Ejecución Secuencial
</label>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary">
<i class="bi bi-check-circle"></i> Guardar Opciones de Backup
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Everything API Tab -->
<div class="tab-pane fade" id="everything" role="tabpanel">
<div class="card">
<div class="card-header">
<h5><i class="bi bi-search"></i> Configuración Everything API</h5>
</div>
<div class="card-body">
<form id="everything-form">
<div class="row">
<div class="col-12">
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="everything_enabled"
{% if config.everything_api.enabled %}checked{% endif %}>
<label class="form-check-label" for="everything_enabled">
<strong>Habilitar Everything API</strong>
</label>
<div class="form-text">Utilizar Everything para búsquedas rápidas de archivos</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label for="dll_path" class="form-label">Ruta de la DLL</label>
<div class="input-group">
<input type="text" class="form-control" id="dll_path"
value="{{ config.everything_api.dll_path }}" required>
<button type="button" class="btn btn-outline-secondary" id="browse-dll">
<i class="bi bi-folder"></i>
</button>
</div>
<div class="form-text">Ruta a Everything32.dll o Everything64.dll</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="search_depth" class="form-label">Profundidad de Búsqueda</label>
<input type="number" class="form-control" id="search_depth"
value="{{ config.everything_api.search_depth }}" min="-1">
<div class="form-text">-1 = Sin límite</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="skip_last_level_for_s7p"
{% if config.everything_api.skip_last_level_for_s7p %}checked{% endif %}>
<label class="form-check-label" for="skip_last_level_for_s7p">
Omitir Último Nivel para S7P
</label>
<div class="form-text">Optimización específica para proyectos Siemens S7</div>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary">
<i class="bi bi-check-circle"></i> Guardar Configuración Everything
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Web Interface Tab -->
<div class="tab-pane fade" id="web" role="tabpanel">
<div class="card">
<div class="card-header">
<h5><i class="bi bi-globe"></i> Configuración Web Interface</h5>
</div>
<div class="card-body">
<form id="web-form">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="web_host" class="form-label">Host</label>
<input type="text" class="form-control" id="web_host"
value="{{ config.web_interface.host }}" required>
<div class="form-text">Dirección IP para el servidor web</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="web_port" class="form-label">Puerto</label>
<input type="number" class="form-control" id="web_port"
value="{{ config.web_interface.port }}" min="1" max="65535" required>
<div class="form-text">Puerto para el servidor web</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="web_debug"
{% if config.web_interface.debug %}checked{% endif %}>
<label class="form-check-label" for="web_debug">
Modo Debug
</label>
<div class="form-text">Habilitar solo para desarrollo</div>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary">
<i class="bi bi-check-circle"></i> Guardar Configuración Web
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Logging Tab -->
<div class="tab-pane fade" id="logging" role="tabpanel">
<div class="card">
<div class="card-header">
<h5><i class="bi bi-file-text"></i> Configuración de Logging</h5>
</div>
<div class="card-body">
<form id="logging-form">
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label for="log_level" class="form-label">Nivel de Log</label>
<select class="form-select" id="log_level">
<option value="DEBUG" {% if config.logging.level == 'DEBUG' %}selected{% endif %}>DEBUG</option>
<option value="INFO" {% if config.logging.level == 'INFO' %}selected{% endif %}>INFO</option>
<option value="WARNING" {% if config.logging.level == 'WARNING' %}selected{% endif %}>WARNING</option>
<option value="ERROR" {% if config.logging.level == 'ERROR' %}selected{% endif %}>ERROR</option>
<option value="CRITICAL" {% if config.logging.level == 'CRITICAL' %}selected{% endif %}>CRITICAL</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="max_log_files" class="form-label">Máximo de Archivos de Log</label>
<input type="number" class="form-control" id="max_log_files"
value="{{ config.logging.max_log_files }}" min="1" max="100">
<div class="form-text">Número máximo de archivos de log a mantener</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="log_rotation_days" class="form-label">Rotación de Logs (días)</label>
<input type="number" class="form-control" id="log_rotation_days"
value="{{ config.logging.log_rotation_days }}" min="1" max="365">
<div class="form-text">Días después de los cuales rotar logs</div>
</div>
</div>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary">
<i class="bi bi-check-circle"></i> Guardar Configuración de Logging
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Add Directory Modal -->
<div class="modal fade" id="addDirectoryModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Agregar Directorio de Observación</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="add-directory-form">
<div class="mb-3">
<label for="new_directory_path" class="form-label">Ruta del Directorio</label>
<div class="input-group">
<input type="text" class="form-control" id="new_directory_path" required>
<button type="button" class="btn btn-outline-secondary" id="browse-new-directory">
<i class="bi bi-folder"></i>
</button>
</div>
</div>
<div class="mb-3">
<label for="new_directory_type" class="form-label">Tipo de Proyecto</label>
<select class="form-select" id="new_directory_type" required>
<option value="siemens_s7">Siemens S7</option>
<option value="siemens_tia">Siemens TIA Portal</option>
<option value="generic">Genérico</option>
</select>
</div>
<div class="mb-3">
<label for="new_directory_description" class="form-label">Descripción</label>
<input type="text" class="form-control" id="new_directory_description"
placeholder="Descripción opcional del directorio">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="button" class="btn btn-primary" onclick="addDirectory()">
<i class="bi bi-plus-circle"></i> Agregar
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
// Configuration management functions
function showAlert(message, type = 'success') {
const alertContainer = document.getElementById('config-alerts');
const alert = document.createElement('div');
alert.className = `alert alert-${type} alert-dismissible fade show`;
alert.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
alertContainer.appendChild(alert);
// Auto-remove after 5 seconds
setTimeout(() => {
if (alert.parentNode) {
alert.remove();
}
}, 5000);
}
// General configuration form
document.getElementById('general-form').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = {
backup_destination: document.getElementById('backup_destination').value,
global_settings: {
default_schedule: document.getElementById('default_schedule').value,
default_schedule_time: document.getElementById('default_schedule_time').value,
scan_interval_minutes: parseInt(document.getElementById('scan_interval_minutes').value),
min_backup_interval_minutes: parseInt(document.getElementById('min_backup_interval_minutes').value),
min_free_space_mb: parseInt(document.getElementById('min_free_space_mb').value),
retry_delay_hours: parseFloat(document.getElementById('retry_delay_hours').value),
backup_timeout_minutes: parseInt(document.getElementById('backup_timeout_minutes').value)
}
};
try {
const response = await fetch('/api/config/general', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (response.ok) {
showAlert('Configuración general guardada correctamente', 'success');
} else {
const error = await response.json();
showAlert('Error al guardar configuración: ' + error.error, 'danger');
}
} catch (error) {
showAlert('Error de conexión: ' + error.message, 'danger');
}
});
// Backup options form
document.getElementById('backup-form').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = {
compression_level: parseInt(document.getElementById('compression_level').value),
hash_algorithm: document.getElementById('hash_algorithm').value,
process_priority: document.getElementById('process_priority').value,
backup_type: document.getElementById('backup_type').value,
filename_format: document.getElementById('filename_format').value,
date_format: document.getElementById('date_format').value,
include_subdirectories: document.getElementById('include_subdirectories').checked,
preserve_directory_structure: document.getElementById('preserve_directory_structure').checked,
sequential_execution: document.getElementById('sequential_execution').checked
};
try {
const response = await fetch('/api/config/backup_options', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (response.ok) {
showAlert('Opciones de backup guardadas correctamente', 'success');
} else {
const error = await response.json();
showAlert('Error al guardar opciones: ' + error.error, 'danger');
}
} catch (error) {
showAlert('Error de conexión: ' + error.message, 'danger');
}
});
// Everything API form
document.getElementById('everything-form').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = {
enabled: document.getElementById('everything_enabled').checked,
dll_path: document.getElementById('dll_path').value,
search_depth: parseInt(document.getElementById('search_depth').value),
skip_last_level_for_s7p: document.getElementById('skip_last_level_for_s7p').checked
};
try {
const response = await fetch('/api/config/everything_api', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (response.ok) {
showAlert('Configuración de Everything API guardada correctamente', 'success');
} else {
const error = await response.json();
showAlert('Error al guardar configuración: ' + error.error, 'danger');
}
} catch (error) {
showAlert('Error de conexión: ' + error.message, 'danger');
}
});
// Web interface form
document.getElementById('web-form').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = {
host: document.getElementById('web_host').value,
port: parseInt(document.getElementById('web_port').value),
debug: document.getElementById('web_debug').checked
};
try {
const response = await fetch('/api/config/web_interface', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (response.ok) {
showAlert('Configuración web guardada correctamente', 'success');
} else {
const error = await response.json();
showAlert('Error al guardar configuración: ' + error.error, 'danger');
}
} catch (error) {
showAlert('Error de conexión: ' + error.message, 'danger');
}
});
// Logging form
document.getElementById('logging-form').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = {
level: document.getElementById('log_level').value,
max_log_files: parseInt(document.getElementById('max_log_files').value),
log_rotation_days: parseInt(document.getElementById('log_rotation_days').value)
};
try {
const response = await fetch('/api/config/logging', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (response.ok) {
showAlert('Configuración de logging guardada correctamente', 'success');
} else {
const error = await response.json();
showAlert('Error al guardar configuración: ' + error.error, 'danger');
}
} catch (error) {
showAlert('Error de conexión: ' + error.message, 'danger');
}
});
// Directory management functions
async function addDirectory() {
const path = document.getElementById('new_directory_path').value;
const type = document.getElementById('new_directory_type').value;
const description = document.getElementById('new_directory_description').value;
if (!path || !type) {
showAlert('Por favor complete los campos requeridos', 'warning');
return;
}
try {
const response = await fetch('/api/config/directories', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
path: path,
type: type,
description: description
})
});
if (response.ok) {
showAlert('Directorio agregado correctamente', 'success');
document.getElementById('add-directory-form').reset();
const modal = bootstrap.Modal.getInstance(document.getElementById('addDirectoryModal'));
modal.hide();
// Reload the page to show the new directory
setTimeout(() => window.location.reload(), 1000);
} else {
const error = await response.json();
showAlert('Error al agregar directorio: ' + error.error, 'danger');
}
} catch (error) {
showAlert('Error de conexión: ' + error.message, 'danger');
}
}
async function removeDirectory(index) {
if (!confirm('¿Está seguro de que desea eliminar este directorio?')) {
return;
}
try {
const response = await fetch(`/api/config/directories/${index}`, {
method: 'DELETE'
});
if (response.ok) {
showAlert('Directorio eliminado correctamente', 'success');
// Reload the page to reflect changes
setTimeout(() => window.location.reload(), 1000);
} else {
const error = await response.json();
showAlert('Error al eliminar directorio: ' + error.error, 'danger');
}
} catch (error) {
showAlert('Error de conexión: ' + error.message, 'danger');
}
}
async function toggleDirectory(index, enabled) {
try {
const response = await fetch(`/api/config/directories/${index}/toggle`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ enabled: enabled })
});
if (response.ok) {
showAlert(`Directorio ${enabled ? 'habilitado' : 'deshabilitado'} correctamente`, 'success');
} else {
const error = await response.json();
showAlert('Error al cambiar estado del directorio: ' + error.error, 'danger');
}
} catch (error) {
showAlert('Error de conexión: ' + error.message, 'danger');
}
}
// File browser functionality using tkinter native dialogs
async function openDirectorySelector(targetInputId, title = 'Seleccionar Directorio') {
try {
// Obtener directorio inicial del input actual
const currentValue = document.getElementById(targetInputId).value;
const initialDir = currentValue || '';
// Encontrar el botón que activó la función
const button = event.target.closest('button');
const originalContent = button.innerHTML;
// Mostrar indicador de carga
button.innerHTML = '<i class="bi bi-clock"></i>';
button.disabled = true;
// Llamar al endpoint que abre el diálogo nativo
const response = await fetch('/api/directories/select', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
initial_dir: initialDir,
title: title
})
});
const data = await response.json();
// Restaurar botón
button.innerHTML = originalContent;
button.disabled = false;
if (!response.ok) {
throw new Error(data.error || 'Error en el servidor');
}
if (data.success && data.selected_path) {
// Usuario seleccionó un directorio
document.getElementById(targetInputId).value = data.selected_path;
showAlert(`Directorio seleccionado: ${data.selected_path}`, 'success');
} else if (data.message) {
// Usuario canceló
showAlert(data.message, 'info');
}
} catch (error) {
// Restaurar botón en caso de error
const button = event.target.closest('button');
if (button && originalContent) {
button.innerHTML = originalContent;
button.disabled = false;
}
showAlert(`Error seleccionando directorio: ${error.message}`, 'danger');
console.error('Directory selection error:', error);
}
}
// File browser button handlers
document.getElementById('browse-backup-dest')?.addEventListener('click', function(event) {
openDirectorySelector('backup_destination', 'Seleccionar Destino de Backups');
});
document.getElementById('browse-dll')?.addEventListener('click', function(event) {
openDirectorySelector('dll_path', 'Seleccionar ubicación de Everything DLL');
});
document.getElementById('browse-new-directory')?.addEventListener('click', function(event) {
openDirectorySelector('new_directory_path', 'Seleccionar Directorio para Observación');
});
</script>
{% endblock %}