mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-17 05:53:15 +02:00
Basic implementation of UI-language switching done
This commit is contained in:
69
internal/locales/de.json
Normal file
69
internal/locales/de.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"page.title": "Fail2ban UI Dashboard",
|
||||
"nav.dashboard": "Dashboard",
|
||||
"nav.filter_debug": "Filter-Debug",
|
||||
"nav.settings": "Einstellungen",
|
||||
"reload_banner.message": "Konfiguration geändert!",
|
||||
"reload_banner.button": "Fail2ban neu laden",
|
||||
"dashboard.title": "Dashboard",
|
||||
"dashboard.overview": "Aktive Jails und Blocks Übersicht",
|
||||
"dashboard.search_label": "Suche gesperrte IPs",
|
||||
"dashboard.search_placeholder": "Geben Sie eine IP-Adresse zum Suchen ein",
|
||||
"dashboard.table.jail_name": "Jail-Name",
|
||||
"dashboard.table.total_banned": "Insgesamt gesperrt",
|
||||
"dashboard.table.new_last_hour": "Neu in letzter Stunde",
|
||||
"dashboard.table.banned_ips": "Gesperrte IPs (Entsperren)",
|
||||
"dashboard.no_jails": "Keine Jails gefunden.",
|
||||
"dashboard.last_bans": "Letzte 5 Sperrvorgänge",
|
||||
"dashboard.table.time": "Zeit",
|
||||
"dashboard.table.jail": "Jail",
|
||||
"dashboard.table.ip": "IP",
|
||||
"dashboard.table.log_line": "Logzeile",
|
||||
"dashboard.no_recent_bans": "Keine aktuellen Sperrvorgänge gefunden.",
|
||||
"dashboard.no_banned_ips": "Keine gesperrten IPs",
|
||||
"dashboard.unban": "Entsperren",
|
||||
"filter_debug.title": "Filter-Debug",
|
||||
"filter_debug.select_filter": "Wählen Sie einen Filter",
|
||||
"filter_debug.log_lines": "Logzeilen",
|
||||
"filter_debug.log_lines_placeholder": "Geben Sie die Logzeilen hier ein...",
|
||||
"filter_debug.test_filter": "Filter testen",
|
||||
"filter_debug.test_results_title": "Testergebnisse",
|
||||
"filter_debug.no_matches": "Keine Übereinstimmungen gefunden.",
|
||||
"settings.title": "Einstellungen",
|
||||
"settings.general": "Allgemeine Einstellungen",
|
||||
"settings.language": "Sprache",
|
||||
"settings.enable_debug": "Debug-Protokoll aktivieren",
|
||||
"settings.alert": "Alarm-Einstellungen",
|
||||
"settings.destination_email": "Ziel-E-Mail (Alarmempfänger)",
|
||||
"settings.destination_email_placeholder": "alerts@swissmakers.ch",
|
||||
"settings.alert_countries": "Alarm-Länder",
|
||||
"settings.alert_countries_description": "Wählen Sie die Länder aus, für die E-Mail-Alarme ausgelöst werden sollen, wenn eine Sperrung erfolgt.",
|
||||
"settings.smtp": "SMTP-Konfiguration",
|
||||
"settings.smtp_host": "SMTP-Host",
|
||||
"settings.smtp_host_placeholder": "z.B. smtp.gmail.com",
|
||||
"settings.smtp_port": "SMTP-Port",
|
||||
"settings.smtp_username": "SMTP-Benutzername",
|
||||
"settings.smtp_username_placeholder": "z.B. user@example.com",
|
||||
"settings.smtp_password": "SMTP-Passwort",
|
||||
"settings.smtp_password_placeholder": "Geben Sie das SMTP-Passwort ein",
|
||||
"settings.smtp_sender": "Absender-E-Mail",
|
||||
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
|
||||
"settings.smtp_tls": "TLS verwenden (empfohlen)",
|
||||
"settings.send_test_email": "Test-E-Mail senden",
|
||||
"settings.fail2ban": "Fail2Ban-Konfiguration",
|
||||
"settings.enable_bantime_increment": "Bantime-Inkrement aktivieren",
|
||||
"settings.default_bantime": "Standard-Bantime",
|
||||
"settings.default_bantime_placeholder": "z.B. 48h",
|
||||
"settings.default_findtime": "Standard-Findtime",
|
||||
"settings.default_findtime_placeholder": "z.B. 30m",
|
||||
"settings.default_max_retry": "Standard-Maximalversuche",
|
||||
"settings.default_max_retry_placeholder": "Geben Sie die maximale Anzahl der Versuche ein",
|
||||
"settings.ignore_ips": "IP-Adressen ignorieren",
|
||||
"settings.ignore_ips_placeholder": "IP-Adressen, getrennt durch Leerzeichen",
|
||||
"settings.save": "Speichern",
|
||||
"modal.filter_config": "Filter-Konfiguration:",
|
||||
"modal.cancel": "Abbrechen",
|
||||
"modal.save": "Speichern",
|
||||
"loading": "Lade..."
|
||||
}
|
||||
|
||||
69
internal/locales/de_ch.json
Normal file
69
internal/locales/de_ch.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"page.title": "Fail2ban UI Dashboard",
|
||||
"nav.dashboard": "Dashboard",
|
||||
"nav.filter_debug": "Filter Debug",
|
||||
"nav.settings": "Istellige",
|
||||
"reload_banner.message": "D'Konfiguration isch gänderet worde!",
|
||||
"reload_banner.button": "Fail2ban neu lade",
|
||||
"dashboard.title": "Dashboard",
|
||||
"dashboard.overview": "Übersicht vo de aktive Jails und Blocks",
|
||||
"dashboard.search_label": "Suech nach g'sperrte IPs",
|
||||
"dashboard.search_placeholder": "Gib d'IP adrässe i, wo du suechsch",
|
||||
"dashboard.table.jail_name": "Jail-Name",
|
||||
"dashboard.table.total_banned": "Insgsamt g'sperrt",
|
||||
"dashboard.table.new_last_hour": "Neu in dr letschte Stund",
|
||||
"dashboard.table.banned_ips": "G'sperrti IPs (Entsperre)",
|
||||
"dashboard.no_jails": "Kei Jails gfunde.",
|
||||
"dashboard.last_bans": "Letschti 5 Sperrvorgäng",
|
||||
"dashboard.table.time": "Zyt",
|
||||
"dashboard.table.jail": "Jail",
|
||||
"dashboard.table.ip": "IP",
|
||||
"dashboard.table.log_line": "Log-Zile",
|
||||
"dashboard.no_recent_bans": "Kei aktuelli Sperrvorgäng gfunde.",
|
||||
"dashboard.no_banned_ips": "Kei g'sperrti IPs",
|
||||
"dashboard.unban": "Entsperre",
|
||||
"filter_debug.title": "Filter Debug",
|
||||
"filter_debug.select_filter": "Wähl en Filter us",
|
||||
"filter_debug.log_lines": "Log-Zile",
|
||||
"filter_debug.log_lines_placeholder": "Gib ä Log-Zile da ii...",
|
||||
"filter_debug.test_filter": "Filter teste",
|
||||
"filter_debug.test_results_title": "Testergebnis",
|
||||
"filter_debug.no_matches": "Kei Übereinstimmige gfunde.",
|
||||
"settings.title": "Istellige",
|
||||
"settings.general": "Allgemeini Istellige",
|
||||
"settings.language": "Sprach",
|
||||
"settings.enable_debug": "Debug-Modus aktivierä",
|
||||
"settings.alert": "Alarm-Istellige",
|
||||
"settings.destination_email": "Ziil-Email (Alarmempfänger)",
|
||||
"settings.destination_email_placeholder": "alerts@swissmakers.ch",
|
||||
"settings.alert_countries": "Alarm-Länder",
|
||||
"settings.alert_countries_description": "Wähl d'Länder us, für weli du per Email ä Alarm becho wetsch, wenn e Sperrig passiert.",
|
||||
"settings.smtp": "SMTP-Konfiguration",
|
||||
"settings.smtp_host": "SMTP-Host",
|
||||
"settings.smtp_host_placeholder": "z.B. smtp.gmail.com",
|
||||
"settings.smtp_port": "SMTP-Port",
|
||||
"settings.smtp_username": "SMTP-Benutzername",
|
||||
"settings.smtp_username_placeholder": "z.B. user@example.com",
|
||||
"settings.smtp_password": "SMTP-Passwort",
|
||||
"settings.smtp_password_placeholder": "Gib s'SMTP-Passwort ii",
|
||||
"settings.smtp_sender": "Absänder-Email",
|
||||
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
|
||||
"settings.smtp_tls": "TLS bruuche (empfohlen)",
|
||||
"settings.send_test_email": "Test-Email schicke",
|
||||
"settings.fail2ban": "Fail2Ban-Konfiguration",
|
||||
"settings.enable_bantime_increment": "Bantime-Inkrement aktivierä",
|
||||
"settings.default_bantime": "Standard-Bantime",
|
||||
"settings.default_bantime_placeholder": "z.B. 48h",
|
||||
"settings.default_findtime": "Standard-Findtime",
|
||||
"settings.default_findtime_placeholder": "z.B. 30m",
|
||||
"settings.default_max_retry": "Standard-Maximalversüech",
|
||||
"settings.default_max_retry_placeholder": "Gib d'maximal Versüech ii",
|
||||
"settings.ignore_ips": "IPs ignorierä",
|
||||
"settings.ignore_ips_placeholder": "IPs, getrennt dur e Leerzeichä",
|
||||
"settings.save": "Speicherä",
|
||||
"modal.filter_config": "Filter-Konfiguration:",
|
||||
"modal.cancel": "Abbräche",
|
||||
"modal.save": "Speicherä",
|
||||
"loading": "Lade..."
|
||||
}
|
||||
|
||||
69
internal/locales/en.json
Normal file
69
internal/locales/en.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"page.title": "Fail2ban UI Dashboard",
|
||||
"nav.dashboard": "Dashboard",
|
||||
"nav.filter_debug": "Filter Debug",
|
||||
"nav.settings": "Settings",
|
||||
"reload_banner.message": "Configuration changed!",
|
||||
"reload_banner.button": "Reload Fail2ban",
|
||||
"dashboard.title": "Dashboard",
|
||||
"dashboard.overview": "Overview active Jails and Blocks",
|
||||
"dashboard.search_label": "Search Banned IPs",
|
||||
"dashboard.search_placeholder": "Enter IP address to search",
|
||||
"dashboard.table.jail_name": "Jail Name",
|
||||
"dashboard.table.total_banned": "Total Banned",
|
||||
"dashboard.table.new_last_hour": "New Last Hour",
|
||||
"dashboard.table.banned_ips": "Banned IPs (Unban)",
|
||||
"dashboard.no_jails": "No jails found.",
|
||||
"dashboard.last_bans": "Last 5 Ban Events",
|
||||
"dashboard.table.time": "Time",
|
||||
"dashboard.table.jail": "Jail",
|
||||
"dashboard.table.ip": "IP",
|
||||
"dashboard.table.log_line": "Log Line",
|
||||
"dashboard.no_recent_bans": "No recent bans found.",
|
||||
"dashboard.no_banned_ips": "No banned IPs",
|
||||
"dashboard.unban": "Unban",
|
||||
"filter_debug.title": "Filter Debug",
|
||||
"filter_debug.select_filter": "Select a Filter",
|
||||
"filter_debug.log_lines": "Log Lines",
|
||||
"filter_debug.log_lines_placeholder": "Enter log lines here...",
|
||||
"filter_debug.test_filter": "Test Filter",
|
||||
"filter_debug.test_results_title": "Test Results",
|
||||
"filter_debug.no_matches": "No matches found.",
|
||||
"settings.title": "Settings",
|
||||
"settings.general": "General Settings",
|
||||
"settings.language": "Language",
|
||||
"settings.enable_debug": "Enable Debug Log",
|
||||
"settings.alert": "Alert Settings",
|
||||
"settings.destination_email": "Destination Email (Alerts Receiver)",
|
||||
"settings.destination_email_placeholder": "alerts@swissmakers.ch",
|
||||
"settings.alert_countries": "Alert Countries",
|
||||
"settings.alert_countries_description": "Choose the countries for which you want to receive email alerts when a block is triggered.",
|
||||
"settings.smtp": "SMTP Configuration",
|
||||
"settings.smtp_host": "SMTP Host",
|
||||
"settings.smtp_host_placeholder": "e.g., smtp.gmail.com",
|
||||
"settings.smtp_port": "SMTP Port",
|
||||
"settings.smtp_username": "SMTP Username",
|
||||
"settings.smtp_username_placeholder": "e.g., user@example.com",
|
||||
"settings.smtp_password": "SMTP Password",
|
||||
"settings.smtp_password_placeholder": "Enter SMTP Password",
|
||||
"settings.smtp_sender": "Sender Email",
|
||||
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
|
||||
"settings.smtp_tls": "Use TLS (Recommended)",
|
||||
"settings.send_test_email": "Send Test Email",
|
||||
"settings.fail2ban": "Fail2Ban Configuration",
|
||||
"settings.enable_bantime_increment": "Enable Bantime Increment",
|
||||
"settings.default_bantime": "Default Bantime",
|
||||
"settings.default_bantime_placeholder": "e.g., 48h",
|
||||
"settings.default_findtime": "Default Findtime",
|
||||
"settings.default_findtime_placeholder": "e.g., 30m",
|
||||
"settings.default_max_retry": "Default Max Retry",
|
||||
"settings.default_max_retry_placeholder": "Enter maximum retries",
|
||||
"settings.ignore_ips": "Ignore IPs",
|
||||
"settings.ignore_ips_placeholder": "IPs to ignore, separated by spaces",
|
||||
"settings.save": "Save",
|
||||
"modal.filter_config": "Filter Config:",
|
||||
"modal.cancel": "Cancel",
|
||||
"modal.save": "Save",
|
||||
"loading": "Loading..."
|
||||
}
|
||||
|
||||
68
internal/locales/es.json
Normal file
68
internal/locales/es.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"page.title": "Panel de control Fail2ban UI",
|
||||
"nav.dashboard": "Panel de control",
|
||||
"nav.filter_debug": "Depuración de filtros",
|
||||
"nav.settings": "Configuración",
|
||||
"reload_banner.message": "¡La configuración ha sido modificada!",
|
||||
"reload_banner.button": "Recargar Fail2ban",
|
||||
"dashboard.title": "Panel de control",
|
||||
"dashboard.overview": "Resumen de Jails y Bloqueos activos",
|
||||
"dashboard.search_label": "Buscar IP bloqueadas",
|
||||
"dashboard.search_placeholder": "Introduce la dirección IP a buscar",
|
||||
"dashboard.table.jail_name": "Nombre del Jail",
|
||||
"dashboard.table.total_banned": "Total bloqueadas",
|
||||
"dashboard.table.new_last_hour": "Nuevas en la última hora",
|
||||
"dashboard.table.banned_ips": "IPs bloqueadas (Desbloquear)",
|
||||
"dashboard.no_jails": "No se encontraron jails.",
|
||||
"dashboard.last_bans": "Últimos 5 eventos de bloqueo",
|
||||
"dashboard.table.time": "Hora",
|
||||
"dashboard.table.jail": "Jail",
|
||||
"dashboard.table.ip": "IP",
|
||||
"dashboard.table.log_line": "Línea de log",
|
||||
"dashboard.no_recent_bans": "No se encontraron bloqueos recientes.",
|
||||
"dashboard.no_banned_ips": "No hay IP bloqueadas",
|
||||
"dashboard.unban": "Desbloquear",
|
||||
"filter_debug.title": "Depuración de filtros",
|
||||
"filter_debug.select_filter": "Selecciona un filtro",
|
||||
"filter_debug.log_lines": "Líneas de log",
|
||||
"filter_debug.log_lines_placeholder": "Introduce las líneas de log aquí...",
|
||||
"filter_debug.test_filter": "Probar filtro",
|
||||
"filter_debug.test_results_title": "Resultados de la prueba",
|
||||
"filter_debug.no_matches": "No se encontraron coincidencias.",
|
||||
"settings.title": "Configuración",
|
||||
"settings.general": "Configuración general",
|
||||
"settings.language": "Idioma",
|
||||
"settings.enable_debug": "Habilitar el modo de depuración",
|
||||
"settings.alert": "Configuración de alertas",
|
||||
"settings.destination_email": "Correo electrónico de destino (receptor de alertas)",
|
||||
"settings.destination_email_placeholder": "alerts@swissmakers.ch",
|
||||
"settings.alert_countries": "Países para alerta",
|
||||
"settings.alert_countries_description": "Elige los países para los que deseas recibir alertas por correo electrónico cuando se produzca un bloqueo.",
|
||||
"settings.smtp": "Configuración SMTP",
|
||||
"settings.smtp_host": "Host SMTP",
|
||||
"settings.smtp_host_placeholder": "p.ej., smtp.gmail.com",
|
||||
"settings.smtp_port": "Puerto SMTP",
|
||||
"settings.smtp_username": "Nombre de usuario SMTP",
|
||||
"settings.smtp_username_placeholder": "p.ej., usuario@example.com",
|
||||
"settings.smtp_password": "Contraseña SMTP",
|
||||
"settings.smtp_password_placeholder": "Introduce la contraseña SMTP",
|
||||
"settings.smtp_sender": "Correo electrónico del remitente",
|
||||
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
|
||||
"settings.smtp_tls": "Usar TLS (recomendado)",
|
||||
"settings.send_test_email": "Enviar correo de prueba",
|
||||
"settings.fail2ban": "Configuración de Fail2Ban",
|
||||
"settings.enable_bantime_increment": "Habilitar incremento de Bantime",
|
||||
"settings.default_bantime": "Bantime por defecto",
|
||||
"settings.default_bantime_placeholder": "p.ej., 48h",
|
||||
"settings.default_findtime": "Findtime por defecto",
|
||||
"settings.default_findtime_placeholder": "p.ej., 30m",
|
||||
"settings.default_max_retry": "Número máximo de reintentos por defecto",
|
||||
"settings.default_max_retry_placeholder": "Introduce el número máximo de reintentos",
|
||||
"settings.ignore_ips": "Ignorar IPs",
|
||||
"settings.ignore_ips_placeholder": "IPs a ignorar, separadas por espacios",
|
||||
"settings.save": "Guardar",
|
||||
"modal.filter_config": "Configuración del filtro:",
|
||||
"modal.cancel": "Cancelar",
|
||||
"modal.save": "Guardar",
|
||||
"loading": "Cargando..."
|
||||
}
|
||||
68
internal/locales/fr.json
Normal file
68
internal/locales/fr.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"page.title": "Tableau de bord Fail2ban UI",
|
||||
"nav.dashboard": "Tableau de bord",
|
||||
"nav.filter_debug": "Débogage des filtres",
|
||||
"nav.settings": "Paramètres",
|
||||
"reload_banner.message": "Configuration modifiée!",
|
||||
"reload_banner.button": "Recharger Fail2ban",
|
||||
"dashboard.title": "Tableau de bord",
|
||||
"dashboard.overview": "Vue d'ensemble des jails et blocages actifs",
|
||||
"dashboard.search_label": "Rechercher des IP bloquées",
|
||||
"dashboard.search_placeholder": "Entrez l'adresse IP à rechercher",
|
||||
"dashboard.table.jail_name": "Nom du Jail",
|
||||
"dashboard.table.total_banned": "Total bloqués",
|
||||
"dashboard.table.new_last_hour": "Nouveaux dans la dernière heure",
|
||||
"dashboard.table.banned_ips": "IPs bloquées (Débloquer)",
|
||||
"dashboard.no_jails": "Aucun jail trouvé.",
|
||||
"dashboard.last_bans": "5 derniers événements de blocage",
|
||||
"dashboard.table.time": "Heure",
|
||||
"dashboard.table.jail": "Jail",
|
||||
"dashboard.table.ip": "IP",
|
||||
"dashboard.table.log_line": "Ligne de log",
|
||||
"dashboard.no_recent_bans": "Aucun blocage récent trouvé.",
|
||||
"dashboard.no_banned_ips": "Aucune IP bloquée",
|
||||
"dashboard.unban": "Débloquer",
|
||||
"filter_debug.title": "Débogage des filtres",
|
||||
"filter_debug.select_filter": "Sélectionnez un filtre",
|
||||
"filter_debug.log_lines": "Lignes de log",
|
||||
"filter_debug.log_lines_placeholder": "Entrez les lignes de log ici...",
|
||||
"filter_debug.test_filter": "Tester le filtre",
|
||||
"filter_debug.test_results_title": "Résultats du test",
|
||||
"filter_debug.no_matches": "Aucune correspondance trouvée.",
|
||||
"settings.title": "Paramètres",
|
||||
"settings.general": "Paramètres généraux",
|
||||
"settings.language": "Langue",
|
||||
"settings.enable_debug": "Activer le mode débogage",
|
||||
"settings.alert": "Paramètres d'alerte",
|
||||
"settings.destination_email": "Email de destination (récepteur des alertes)",
|
||||
"settings.destination_email_placeholder": "alerts@swissmakers.ch",
|
||||
"settings.alert_countries": "Pays d'alerte",
|
||||
"settings.alert_countries_description": "Choisissez les pays pour lesquels vous souhaitez recevoir des alertes par email lors d'un blocage.",
|
||||
"settings.smtp": "Configuration SMTP",
|
||||
"settings.smtp_host": "Hôte SMTP",
|
||||
"settings.smtp_host_placeholder": "par exemple, smtp.gmail.com",
|
||||
"settings.smtp_port": "Port SMTP",
|
||||
"settings.smtp_username": "Nom d'utilisateur SMTP",
|
||||
"settings.smtp_username_placeholder": "par exemple, utilisateur@example.com",
|
||||
"settings.smtp_password": "Mot de passe SMTP",
|
||||
"settings.smtp_password_placeholder": "Entrez le mot de passe SMTP",
|
||||
"settings.smtp_sender": "Email de l'expéditeur",
|
||||
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
|
||||
"settings.smtp_tls": "Utiliser TLS (recommandé)",
|
||||
"settings.send_test_email": "Envoyer un email de test",
|
||||
"settings.fail2ban": "Configuration Fail2Ban",
|
||||
"settings.enable_bantime_increment": "Activer l'incrémentation du Bantime",
|
||||
"settings.default_bantime": "Bantime par défaut",
|
||||
"settings.default_bantime_placeholder": "par exemple, 48h",
|
||||
"settings.default_findtime": "Findtime par défaut",
|
||||
"settings.default_findtime_placeholder": "par exemple, 30m",
|
||||
"settings.default_max_retry": "Nombre maximal de réessais par défaut",
|
||||
"settings.default_max_retry_placeholder": "Entrez le nombre maximal de réessais",
|
||||
"settings.ignore_ips": "Ignorer les IPs",
|
||||
"settings.ignore_ips_placeholder": "IPs à ignorer, séparées par des espaces",
|
||||
"settings.save": "Enregistrer",
|
||||
"modal.filter_config": "Configuration du filtre:",
|
||||
"modal.cancel": "Annuler",
|
||||
"modal.save": "Enregistrer",
|
||||
"loading": "Chargement..."
|
||||
}
|
||||
68
internal/locales/it.json
Normal file
68
internal/locales/it.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"page.title": "Cruscotto Fail2ban UI",
|
||||
"nav.dashboard": "Cruscotto",
|
||||
"nav.filter_debug": "Debug Filtro",
|
||||
"nav.settings": "Impostazioni",
|
||||
"reload_banner.message": "Configurazione modificata!",
|
||||
"reload_banner.button": "Ricarica Fail2ban",
|
||||
"dashboard.title": "Cruscotto",
|
||||
"dashboard.overview": "Panoramica dei jail e dei blocchi attivi",
|
||||
"dashboard.search_label": "Cerca IP bloccate",
|
||||
"dashboard.search_placeholder": "Inserisci l'indirizzo IP da cercare",
|
||||
"dashboard.table.jail_name": "Nome del Jail",
|
||||
"dashboard.table.total_banned": "Totale bloccate",
|
||||
"dashboard.table.new_last_hour": "Nuove nell'ultima ora",
|
||||
"dashboard.table.banned_ips": "IP bloccate (Sblocca)",
|
||||
"dashboard.no_jails": "Nessun jail trovato.",
|
||||
"dashboard.last_bans": "Ultimi 5 eventi di blocco",
|
||||
"dashboard.table.time": "Ora",
|
||||
"dashboard.table.jail": "Jail",
|
||||
"dashboard.table.ip": "IP",
|
||||
"dashboard.table.log_line": "Riga di log",
|
||||
"dashboard.no_recent_bans": "Nessun blocco recente trovato.",
|
||||
"dashboard.no_banned_ips": "Nessuna IP bloccata",
|
||||
"dashboard.unban": "Sblocca",
|
||||
"filter_debug.title": "Debug Filtro",
|
||||
"filter_debug.select_filter": "Seleziona un filtro",
|
||||
"filter_debug.log_lines": "Righe di log",
|
||||
"filter_debug.log_lines_placeholder": "Inserisci qui le righe di log...",
|
||||
"filter_debug.test_filter": "Testa filtro",
|
||||
"filter_debug.test_results_title": "Risultati del test",
|
||||
"filter_debug.no_matches": "Nessuna corrispondenza trovata.",
|
||||
"settings.title": "Impostazioni",
|
||||
"settings.general": "Impostazioni generali",
|
||||
"settings.language": "Lingua",
|
||||
"settings.enable_debug": "Abilita debug",
|
||||
"settings.alert": "Impostazioni di allarme",
|
||||
"settings.destination_email": "Email di destinazione (ricevente allarmi)",
|
||||
"settings.destination_email_placeholder": "alerts@swissmakers.ch",
|
||||
"settings.alert_countries": "Paesi per allarme",
|
||||
"settings.alert_countries_description": "Seleziona i paesi per i quali desideri ricevere allarmi via email quando si verifica un blocco.",
|
||||
"settings.smtp": "Configurazione SMTP",
|
||||
"settings.smtp_host": "Host SMTP",
|
||||
"settings.smtp_host_placeholder": "es. smtp.gmail.com",
|
||||
"settings.smtp_port": "Porta SMTP",
|
||||
"settings.smtp_username": "Nome utente SMTP",
|
||||
"settings.smtp_username_placeholder": "es. utente@example.com",
|
||||
"settings.smtp_password": "Password SMTP",
|
||||
"settings.smtp_password_placeholder": "Inserisci la password SMTP",
|
||||
"settings.smtp_sender": "Email del mittente",
|
||||
"settings.smtp_sender_placeholder": "noreply@swissmakers.ch",
|
||||
"settings.smtp_tls": "Usa TLS (raccomandato)",
|
||||
"settings.send_test_email": "Invia email di test",
|
||||
"settings.fail2ban": "Configurazione Fail2Ban",
|
||||
"settings.enable_bantime_increment": "Abilita incremento del Bantime",
|
||||
"settings.default_bantime": "Bantime predefinito",
|
||||
"settings.default_bantime_placeholder": "es. 48h",
|
||||
"settings.default_findtime": "Findtime predefinito",
|
||||
"settings.default_findtime_placeholder": "es. 30m",
|
||||
"settings.default_max_retry": "Numero massimo di tentativi predefinito",
|
||||
"settings.default_max_retry_placeholder": "Inserisci il numero massimo di tentativi",
|
||||
"settings.ignore_ips": "Ignora IP",
|
||||
"settings.ignore_ips_placeholder": "IP da ignorare, separate da spazi",
|
||||
"settings.save": "Salva",
|
||||
"modal.filter_config": "Configurazione del filtro:",
|
||||
"modal.cancel": "Annulla",
|
||||
"modal.save": "Salva",
|
||||
"loading": "Caricamento..."
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||
<title>Fail2ban UI Dashboard</title>
|
||||
<title data-i18n="page.title">Fail2ban UI Dashboard</title>
|
||||
<!-- Bootstrap 5 (CDN) -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<!-- Select2 CSS -->
|
||||
@@ -31,15 +31,18 @@
|
||||
#loading-overlay {
|
||||
display: none; /* hidden by default */
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.5);
|
||||
z-index: 9999; /* on top */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.spinner-border {
|
||||
width: 4rem; height: 4rem;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
/* Reload banner */
|
||||
@@ -49,7 +52,8 @@
|
||||
|
||||
/* Improve table readability on small screens */
|
||||
@media (max-width: 575px) {
|
||||
.table th, .table td {
|
||||
.table th,
|
||||
.table td {
|
||||
font-size: 0.8rem;
|
||||
padding: 5px;
|
||||
}
|
||||
@@ -61,105 +65,108 @@
|
||||
</head>
|
||||
|
||||
<body class="bg-light">
|
||||
<!-- ******************************************************************* -->
|
||||
<!-- NAVIGATION : -->
|
||||
<!-- ******************************************************************* -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<!-- ******************************************************************* -->
|
||||
<!-- NAVIGATION : -->
|
||||
<!-- ******************************************************************* -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">
|
||||
<strong>Fail2ban UI</strong>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" onclick="showSection('dashboardSection')">Dashboard</a>
|
||||
<a class="nav-link" href="#" onclick="showSection('dashboardSection')" data-i18n="nav.dashboard">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" onclick="showSection('filterSection')">Filter Debug</a>
|
||||
<a class="nav-link" href="#" onclick="showSection('filterSection')" data-i18n="nav.filter_debug">Filter Debug</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" onclick="showSection('settingsSection')">Settings</a>
|
||||
<a class="nav-link" href="#" onclick="showSection('settingsSection')" data-i18n="nav.settings">Settings</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<!-- ******************************************************************* -->
|
||||
</nav>
|
||||
<!-- ******************************************************************* -->
|
||||
|
||||
<!-- Reload Banner -->
|
||||
<div id="reloadBanner" class="bg-warning text-dark p-3 text-center">
|
||||
<strong>Configuration changed! </strong>
|
||||
<button class="btn btn-dark" onclick="reloadFail2ban()">
|
||||
Reload Fail2ban
|
||||
</button>
|
||||
</div>
|
||||
<!-- Reload Banner -->
|
||||
<div id="reloadBanner" class="bg-warning text-dark p-3 text-center">
|
||||
<strong data-i18n="reload_banner.message">Configuration changed!</strong>
|
||||
<button class="btn btn-dark" onclick="reloadFail2ban()" data-i18n="reload_banner.button">Reload Fail2ban</button>
|
||||
</div>
|
||||
|
||||
<!-- ******************************************************************* -->
|
||||
<!-- APP Sections (Pages) : -->
|
||||
<!-- ******************************************************************* -->
|
||||
<!-- ******************************************************************* -->
|
||||
<!-- APP Sections (Pages) : -->
|
||||
<!-- ******************************************************************* -->
|
||||
|
||||
<!-- Dashboard Section -->
|
||||
<div id="dashboardSection" class="container my-4">
|
||||
<h1 class="mb-4">Dashboard</h1>
|
||||
<!-- Dashboard Section -->
|
||||
<div id="dashboardSection" class="container my-4">
|
||||
<h1 class="mb-4" data-i18n="dashboard.title">Dashboard</h1>
|
||||
<div id="dashboard"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Debug Section -->
|
||||
<div id="filterSection" style="display: none;" class="container my-4">
|
||||
<h2>Filter Debug</h2>
|
||||
<!-- Filter Debug Section -->
|
||||
<div id="filterSection" style="display: none;" class="container my-4">
|
||||
<h2 data-i18n="filter_debug.title">Filter Debug</h2>
|
||||
<!-- Dropdown of available jail/filters -->
|
||||
<div class="mb-3">
|
||||
<label for="filterSelect" class="form-label">Select a Filter</label>
|
||||
<label for="filterSelect" class="form-label" data-i18n="filter_debug.select_filter">Select a Filter</label>
|
||||
<select id="filterSelect" class="form-select"></select>
|
||||
</div>
|
||||
<!-- Textarea for log lines to test -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Log Lines</label>
|
||||
<textarea id="logLinesTextarea" class="form-control" rows="6" disabled></textarea>
|
||||
<label class="form-label" data-i18n="filter_debug.log_lines">Log Lines</label>
|
||||
<textarea id="logLinesTextarea" class="form-control" rows="6" disabled
|
||||
data-i18n-placeholder="filter_debug.log_lines_placeholder" placeholder="Enter log lines here..."></textarea>
|
||||
</div>
|
||||
<button class="btn btn-secondary" onclick="testSelectedFilter()">Test Filter</button>
|
||||
<button class="btn btn-secondary" onclick="testSelectedFilter()" data-i18n="filter_debug.test_filter">Test Filter</button>
|
||||
<hr/>
|
||||
<div id="testResults"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Section -->
|
||||
<div id="settingsSection" style="display: none;" class="container my-4">
|
||||
<h2>Settings</h2>
|
||||
<!-- Settings Section -->
|
||||
<div id="settingsSection" style="display: none;" class="container my-4">
|
||||
<h2 data-i18n="settings.title">Settings</h2>
|
||||
<form onsubmit="saveSettings(event)">
|
||||
<!-- General Settings Group -->
|
||||
<fieldset class="border p-3 rounded mb-4">
|
||||
<legend class="w-auto px-2">General Settings</legend>
|
||||
<legend class="w-auto px-2" data-i18n="settings.general">General Settings</legend>
|
||||
<!-- Language Selection -->
|
||||
<div class="mb-3">
|
||||
<label for="languageSelect" class="form-label">Language</label>
|
||||
<select id="languageSelect" class="form-select" disabled>
|
||||
<label for="languageSelect" class="form-label" data-i18n="settings.language">Language</label>
|
||||
<select id="languageSelect" class="form-select">
|
||||
<option value="en">English</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="it">Italiano</option>
|
||||
<option value="de_ch">Schwiizerdütsch</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Debug Log Output -->
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="debugMode">
|
||||
<label for="debugMode" class="form-check-label">Enable Debug Log</label>
|
||||
<label for="debugMode" class="form-check-label" data-i18n="settings.enable_debug">Enable Debug Log</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- Alert Settings Group -->
|
||||
<fieldset class="border p-3 rounded mb-4">
|
||||
<legend class="w-auto px-2">Alert Settings</legend>
|
||||
<legend class="w-auto px-2" data-i18n="settings.alert">Alert Settings</legend>
|
||||
<div class="mb-3">
|
||||
<label for="destEmail" class="form-label">Destination Email (Alerts Receiver)</label>
|
||||
<input type="email" class="form-control" id="destEmail" placeholder="alerts@swissmakers.ch" />
|
||||
<label for="destEmail" class="form-label" data-i18n="settings.destination_email">Destination Email (Alerts Receiver)</label>
|
||||
<input type="email" class="form-control" id="destEmail"
|
||||
data-i18n-placeholder="settings.destination_email_placeholder" placeholder="alerts@swissmakers.ch" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="alertCountries" class="form-label">Alert Countries</label>
|
||||
<p class="text-muted">
|
||||
<label for="alertCountries" class="form-label" data-i18n="settings.alert_countries">Alert Countries</label>
|
||||
<p class="text-muted" data-i18n="settings.alert_countries_description">
|
||||
Choose the countries for which you want to receive email alerts when a block is triggered.
|
||||
</p>
|
||||
<select id="alertCountries" class="form-select" multiple>
|
||||
@@ -365,165 +372,164 @@
|
||||
|
||||
<!-- SMTP Configuration Group -->
|
||||
<fieldset class="border p-3 rounded mb-4">
|
||||
<legend class="w-auto px-2">SMTP Configuration</legend>
|
||||
<legend class="w-auto px-2" data-i18n="settings.smtp">SMTP Configuration</legend>
|
||||
<div class="mb-3">
|
||||
<label for="smtpHost" class="form-label">SMTP Host</label>
|
||||
<input type="text" class="form-control" id="smtpHost" placeholder="e.g., smtp.gmail.com" required />
|
||||
<label for="smtpHost" class="form-label" data-i18n="settings.smtp_host">SMTP Host</label>
|
||||
<input type="text" class="form-control" id="smtpHost"
|
||||
data-i18n-placeholder="settings.smtp_host_placeholder" placeholder="e.g., smtp.gmail.com" required />
|
||||
</div>
|
||||
<label for="smtpPort">SMTP Port</label>
|
||||
<label for="smtpPort" data-i18n="settings.smtp_port">SMTP Port</label>
|
||||
<select id="smtpPort" class="form-select">
|
||||
<option value="587" selected>587 (Recommended - STARTTLS)</option>
|
||||
<option value="465" disabled>465 (Not Supported)</option>
|
||||
</select>
|
||||
<div class="mb-3">
|
||||
<label for="smtpUsername" class="form-label">SMTP Username</label>
|
||||
<input type="text" class="form-control" id="smtpUsername" placeholder="e.g., user@example.com" required />
|
||||
<label for="smtpUsername" class="form-label" data-i18n="settings.smtp_username">SMTP Username</label>
|
||||
<input type="text" class="form-control" id="smtpUsername"
|
||||
data-i18n-placeholder="settings.smtp_username_placeholder" placeholder="e.g., user@example.com" required />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="smtpPassword" class="form-label">SMTP Password</label>
|
||||
<input type="password" class="form-control" id="smtpPassword" placeholder="Enter SMTP Password" required />
|
||||
<label for="smtpPassword" class="form-label" data-i18n="settings.smtp_password">SMTP Password</label>
|
||||
<input type="password" class="form-control" id="smtpPassword"
|
||||
data-i18n-placeholder="settings.smtp_password_placeholder" placeholder="Enter SMTP Password" required />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="smtpFrom" class="form-label">Sender Email</label>
|
||||
<input type="email" class="form-control" id="smtpFrom" placeholder="noreply@swissmakers.ch" required />
|
||||
<label for="smtpFrom" class="form-label" data-i18n="settings.smtp_sender">Sender Email</label>
|
||||
<input type="email" class="form-control" id="smtpFrom"
|
||||
data-i18n-placeholder="settings.smtp_sender_placeholder" placeholder="noreply@swissmakers.ch" required />
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="smtpUseTLS">
|
||||
<label for="smtpUseTLS" class="form-check-label">Use TLS (Recommended)</label>
|
||||
<label for="smtpUseTLS" class="form-check-label" data-i18n="settings.smtp_tls">Use TLS (Recommended)</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary mt-2" onclick="sendTestEmail()">Send Test Email</button>
|
||||
<button type="button" class="btn btn-secondary mt-2" onclick="sendTestEmail()" data-i18n="settings.send_test_email">Send Test Email</button>
|
||||
</fieldset>
|
||||
|
||||
<!-- Fail2Ban Configuration Group -->
|
||||
<fieldset class="border p-3 rounded mb-4">
|
||||
<legend class="w-auto px-2">Fail2Ban Configuration</legend>
|
||||
|
||||
<legend class="w-auto px-2" data-i18n="settings.fail2ban">Fail2Ban Configuration</legend>
|
||||
<!-- Bantime Increment -->
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="bantimeIncrement" />
|
||||
<label for="bantimeIncrement" class="form-check-label">Enable Bantime Increment</label>
|
||||
<label for="bantimeIncrement" class="form-check-label" data-i18n="settings.enable_bantime_increment">Enable Bantime Increment</label>
|
||||
</div>
|
||||
<!-- Bantime -->
|
||||
<div class="mb-3">
|
||||
<label for="banTime" class="form-label">Default Bantime</label>
|
||||
<input type="text" class="form-control" id="banTime" placeholder="e.g., 48h" />
|
||||
<label for="banTime" class="form-label" data-i18n="settings.default_bantime">Default Bantime</label>
|
||||
<input type="text" class="form-control" id="banTime"
|
||||
data-i18n-placeholder="settings.default_bantime_placeholder" placeholder="e.g., 48h" />
|
||||
</div>
|
||||
<!-- Findtime -->
|
||||
<div class="mb-3">
|
||||
<label for="findTime" class="form-label">Default Findtime</label>
|
||||
<input type="text" class="form-control" id="findTime" placeholder="e.g., 30m" />
|
||||
<label for="findTime" class="form-label" data-i18n="settings.default_findtime">Default Findtime</label>
|
||||
<input type="text" class="form-control" id="findTime"
|
||||
data-i18n-placeholder="settings.default_findtime_placeholder" placeholder="e.g., 30m" />
|
||||
</div>
|
||||
<!-- Max Retry -->
|
||||
<div class="mb-3">
|
||||
<label for="maxRetry" class="form-label">Default Max Retry</label>
|
||||
<input type="number" class="form-control" id="maxRetry" placeholder="Enter maximum retries" />
|
||||
<label for="maxRetry" class="form-label" data-i18n="settings.default_max_retry">Default Max Retry</label>
|
||||
<input type="number" class="form-control" id="maxRetry"
|
||||
data-i18n-placeholder="settings.default_max_retry_placeholder" placeholder="Enter maximum retries" />
|
||||
</div>
|
||||
<!-- Ignore IPs -->
|
||||
<div class="mb-3">
|
||||
<label for="ignoreIP" class="form-label">Ignore IPs</label>
|
||||
<textarea class="form-control" id="ignoreIP" rows="2" placeholder="IPs to ignore, separated by spaces"></textarea>
|
||||
<label for="ignoreIP" class="form-label" data-i18n="settings.ignore_ips">Ignore IPs</label>
|
||||
<textarea class="form-control" id="ignoreIP" rows="2"
|
||||
data-i18n-placeholder="settings.ignore_ips_placeholder" placeholder="IPs to ignore, separated by spaces"></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<button type="submit" class="btn btn-primary" data-i18n="settings.save">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
<!-- ******************************************************************* -->
|
||||
</div>
|
||||
<!-- ******************************************************************* -->
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="text-center mt-4 mb-4">
|
||||
<!-- Footer -->
|
||||
<footer class="text-center mt-4 mb-4">
|
||||
<p class="mb-0">
|
||||
© <a href="https://swissmakers.ch" target="_blank">Swissmakers GmbH</a>
|
||||
-
|
||||
<a href="https://github.com/swissmakers/fail2ban-ui" target="_blank">
|
||||
GitHub
|
||||
</a>
|
||||
<a href="https://github.com/swissmakers/fail2ban-ui" target="_blank">GitHub</a>
|
||||
</p>
|
||||
</footer>
|
||||
</footer>
|
||||
|
||||
<!-- ******************************************************************* -->
|
||||
<!-- APP Components (HTML) : -->
|
||||
<!-- ******************************************************************* -->
|
||||
<!-- Loading Overlay -->
|
||||
<div id="loading-overlay" class="d-flex">
|
||||
<!-- ******************************************************************* -->
|
||||
<!-- APP Components (HTML) : -->
|
||||
<!-- ******************************************************************* -->
|
||||
<!-- Loading Overlay -->
|
||||
<div id="loading-overlay" class="d-flex">
|
||||
<div class="spinner-border text-light" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
<span class="visually-hidden" data-i18n="loading">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Jail Config Modal -->
|
||||
<div class="modal fade" id="jailConfigModal" tabindex="-1" aria-hidden="true">
|
||||
<!-- Jail Config Modal -->
|
||||
<div class="modal fade" id="jailConfigModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
Filter Config: <span id="modalJailName"></span>
|
||||
<span data-i18n="modal.filter_config">Filter Config:</span> <span id="modalJailName"></span>
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||||
aria-label="Close"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<textarea id="jailConfigTextarea" class="form-control" rows="15"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary"
|
||||
data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveJailConfig()">
|
||||
Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" data-i18n="modal.cancel">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveJailConfig()" data-i18n="modal.save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ******************************************************************* -->
|
||||
</div>
|
||||
<!-- ******************************************************************* -->
|
||||
|
||||
<!-- Bootstrap 5 JS (for modal, etc.) -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js">
|
||||
</script>
|
||||
<!-- jQuery (used by Select2) -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- Select2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/js/select2.min.js"></script>
|
||||
<!-- Bootstrap 5 JS (for modal, etc.) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- jQuery (used by Select2) -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- Select2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/js/select2.min.js"></script>
|
||||
|
||||
<script>
|
||||
// For information: We avoid ES6 backticks in our JS, to prevent confusion with the Go template parser.
|
||||
"use strict";
|
||||
<script>
|
||||
// For information: We avoid ES6 backticks in our JS, to prevent confusion with the Go template parser.
|
||||
"use strict";
|
||||
|
||||
//*******************************************************************
|
||||
//* Init page and main-components : *
|
||||
//*******************************************************************
|
||||
//*******************************************************************
|
||||
//* Init page and main-components : *
|
||||
//*******************************************************************
|
||||
|
||||
// Init and run first function, when DOM is ready
|
||||
var currentJailForConfig = null;
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
var currentJailForConfig = null;
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
showLoading(true);
|
||||
checkReloadNeeded();
|
||||
fetchSummary().then(function() {
|
||||
showLoading(false);
|
||||
initializeTooltips(); // Initialize tooltips after fetching and rendering
|
||||
getTranslationsSettingsOnPageload();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Function to initialize Bootstrap tooltips
|
||||
function initializeTooltips() {
|
||||
// Function to initialize Bootstrap tooltips
|
||||
function initializeTooltips() {
|
||||
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
tooltipTriggerList.forEach(function (tooltipTriggerEl) {
|
||||
new bootstrap.Tooltip(tooltipTriggerEl);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle the loading overlay (with !important)
|
||||
function showLoading(show) {
|
||||
// Toggle the loading overlay (with !important)
|
||||
function showLoading(show) {
|
||||
var overlay = document.getElementById('loading-overlay');
|
||||
if (show) {
|
||||
overlay.style.setProperty('display', 'flex', 'important');
|
||||
} else {
|
||||
overlay.style.setProperty('display', 'none', 'important');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is still a reload of the fail2ban service needed
|
||||
function checkReloadNeeded() {
|
||||
// Check if there is still a reload of the fail2ban service needed
|
||||
function checkReloadNeeded() {
|
||||
fetch('/api/settings')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
@@ -534,10 +540,10 @@ function checkReloadNeeded() {
|
||||
}
|
||||
})
|
||||
.catch(err => console.error('Error checking reloadNeeded:', err));
|
||||
}
|
||||
}
|
||||
|
||||
// Load dynamically the other pages when navigating in nav
|
||||
function showSection(sectionId) {
|
||||
// Load dynamically the other pages when navigating in nav
|
||||
function showSection(sectionId) {
|
||||
// hide all sections
|
||||
document.getElementById('dashboardSection').style.display = 'none';
|
||||
document.getElementById('filterSection').style.display = 'none';
|
||||
@@ -561,14 +567,14 @@ function showSection(sectionId) {
|
||||
let navbarToggler = document.querySelector('.navbar-toggler');
|
||||
navbarToggler.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************************************
|
||||
//* Fetch data and render dashboard : *
|
||||
//*******************************************************************
|
||||
//*******************************************************************
|
||||
//* Fetch data and render dashboard : *
|
||||
//*******************************************************************
|
||||
|
||||
// Fetch summary (jails, stats, last bans)
|
||||
function fetchSummary() {
|
||||
// Fetch summary (jails, stats, last bans)
|
||||
function fetchSummary() {
|
||||
return fetch('/api/summary')
|
||||
.then(function(res) { return res.json(); })
|
||||
.then(function(data) {
|
||||
@@ -583,35 +589,35 @@ function fetchSummary() {
|
||||
document.getElementById('dashboard').innerHTML =
|
||||
'<div class="alert alert-danger">Error: ' + err + '</div>';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Render the main dashboard
|
||||
function renderDashboard(data) {
|
||||
// Render the main dashboard
|
||||
function renderDashboard(data) {
|
||||
var html = "";
|
||||
|
||||
// Add a search bar
|
||||
html += `
|
||||
<fieldset class="border p-3 rounded mb-4">
|
||||
<legend class="w-auto px-2"><span data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="The Overview displays the currently enabled jails that you have added to your jail.local configuration.">Overview active Jails and Blocks</span></legend>
|
||||
<legend class="w-auto px-2"><span data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="The Overview displays the currently enabled jails that you have added to your jail.local configuration." data-i18n="dashboard.overview">Overview active Jails and Blocks</span></legend>
|
||||
<div class="mb-3">
|
||||
<label for="ipSearch" class="form-label">Search Banned IPs</label>
|
||||
<input type="text" id="ipSearch" class="form-control" placeholder="Enter IP address to search" onkeyup="filterIPs()">
|
||||
<label for="ipSearch" class="form-label" data-i18n="dashboard.search_label">Search Banned IPs</label>
|
||||
<input type="text" id="ipSearch" class="form-control" placeholder="Enter IP address to search" data-i18n-placeholder="dashboard.search_placeholder" onkeyup="filterIPs()">
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Jails table
|
||||
if (!data.jails || data.jails.length === 0) {
|
||||
html += '<p>No jails found.</p>';
|
||||
html += '<p data-i18n="dashboard.no_jails">No jails found.</p>';
|
||||
} else {
|
||||
html += ''
|
||||
+ '<div class="table-responsive">'
|
||||
+ '<table class="table table-striped" id="jailsTable">'
|
||||
+ ' <thead>'
|
||||
+ ' <tr>'
|
||||
+ ' <th>Jail Name</th>'
|
||||
+ ' <th>Total Banned</th>'
|
||||
+ ' <th>New Last Hour</th>'
|
||||
+ ' <th>Banned IPs (Unban)</th>'
|
||||
+ ' <th data-i18n="dashboard.table.jail_name">Jail Name</th>'
|
||||
+ ' <th data-i18n="dashboard.table.total_banned">Total Banned</th>'
|
||||
+ ' <th data-i18n="dashboard.table.new_last_hour">New Last Hour</th>'
|
||||
+ ' <th data-i18n="dashboard.table.banned_ips">Banned IPs (Unban)</th>'
|
||||
+ ' </tr>'
|
||||
+ ' </thead>'
|
||||
+ ' <tbody>';
|
||||
@@ -637,19 +643,19 @@ function renderDashboard(data) {
|
||||
|
||||
// Last 5 bans
|
||||
html += '<fieldset class="border p-3 rounded mb-4">';
|
||||
html += ' <legend class="w-auto px-2">Last 5 Ban Events</legend>';
|
||||
html += ' <legend class="w-auto px-2" data-i18n="dashboard.last_bans">Last 5 Ban Events</legend>';
|
||||
if (!data.lastBans || data.lastBans.length === 0) {
|
||||
html += '<p>No recent bans found.</p>';
|
||||
html += '<p data-i18n="dashboard.no_recent_bans">No recent bans found.</p>';
|
||||
} else {
|
||||
html += ''
|
||||
+ '<div class="table-responsive">'
|
||||
+ '<table class="table table-bordered">'
|
||||
+ ' <thead>'
|
||||
+ ' <tr>'
|
||||
+ ' <th>Time</th>'
|
||||
+ ' <th>Jail</th>'
|
||||
+ ' <th>IP</th>'
|
||||
+ ' <th>Log Line</th>'
|
||||
+ ' <th data-i18n="dashboard.table.time">Time</th>'
|
||||
+ ' <th data-i18n="dashboard.table.jail">Jail</th>'
|
||||
+ ' <th data-i18n="dashboard.table.ip">IP</th>'
|
||||
+ ' <th data-i18n="dashboard.table.log_line">Log Line</th>'
|
||||
+ ' </tr>'
|
||||
+ ' </thead>'
|
||||
+ ' <tbody></div>';
|
||||
@@ -668,12 +674,12 @@ function renderDashboard(data) {
|
||||
}
|
||||
|
||||
document.getElementById('dashboard').innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
// Render banned IPs with "Unban" button
|
||||
function renderBannedIPs(jailName, ips) {
|
||||
// Render banned IPs with "Unban" button
|
||||
function renderBannedIPs(jailName, ips) {
|
||||
if (!ips || ips.length === 0) {
|
||||
return '<em>No banned IPs</em>';
|
||||
return '<em data-i18n="dashboard.no_banned_ips">No banned IPs</em>';
|
||||
}
|
||||
var content = '<ul class="list-unstyled mb-0">';
|
||||
ips.forEach(function(ip) {
|
||||
@@ -682,53 +688,48 @@ function renderBannedIPs(jailName, ips) {
|
||||
+ ' <span class="me-auto">' + ip + '</span>'
|
||||
+ ' <button class="btn btn-sm btn-warning"'
|
||||
+ ' onclick="unbanIP(\'' + jailName + '\', \'' + ip + '\')">'
|
||||
+ ' Unban'
|
||||
+ ' <span data-i18n="dashboard.unban">Unban</span>'
|
||||
+ ' </button>'
|
||||
+ '</li>';
|
||||
});
|
||||
content += '</ul>';
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter IPs on dashboard table
|
||||
function filterIPs() {
|
||||
const query = document.getElementById("ipSearch").value.toLowerCase(); // Get the search query
|
||||
const rows = document.querySelectorAll("#jailsTable .jail-row"); // Get all jail rows
|
||||
// Filter IPs on dashboard table
|
||||
function filterIPs() {
|
||||
const query = document.getElementById("ipSearch").value.toLowerCase();
|
||||
const rows = document.querySelectorAll("#jailsTable .jail-row");
|
||||
|
||||
rows.forEach((row) => {
|
||||
const ipSpans = row.querySelectorAll("ul li span"); // Find all IP span elements in this row
|
||||
let matchFound = false; // Reset match flag for the row
|
||||
const ipSpans = row.querySelectorAll("ul li span");
|
||||
let matchFound = false;
|
||||
|
||||
ipSpans.forEach((span) => {
|
||||
const originalText = span.textContent; // The full original text
|
||||
const originalText = span.textContent;
|
||||
const ipText = originalText.toLowerCase();
|
||||
|
||||
if (query && ipText.includes(query)) {
|
||||
matchFound = true; // Match found in this row
|
||||
|
||||
// Highlight the matching part
|
||||
matchFound = true;
|
||||
const highlightedText = originalText.replace(
|
||||
new RegExp(query, "gi"), // Case-insensitive match
|
||||
(match) => `<mark>${match}</mark>` // Wrap match in <mark>
|
||||
new RegExp(query, "gi"),
|
||||
(match) => `<mark>${match}</mark>`
|
||||
);
|
||||
span.innerHTML = highlightedText; // Update span's HTML with highlighting
|
||||
span.innerHTML = highlightedText;
|
||||
} else {
|
||||
// Remove highlighting if no match or search is cleared
|
||||
span.innerHTML = originalText;
|
||||
}
|
||||
});
|
||||
|
||||
// Show the row if a match is found or the query is empty
|
||||
row.style.display = matchFound || !query ? "" : "none";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************************************
|
||||
//* Functions to manage IP-bans : *
|
||||
//*******************************************************************
|
||||
//*******************************************************************
|
||||
//* Functions to manage IP-bans : *
|
||||
//*******************************************************************
|
||||
|
||||
// Unban IP
|
||||
function unbanIP(jail, ip) {
|
||||
function unbanIP(jail, ip) {
|
||||
if (!confirm("Unban IP " + ip + " from jail " + jail + "?")) {
|
||||
return;
|
||||
}
|
||||
@@ -749,14 +750,13 @@ function unbanIP(jail, ip) {
|
||||
.finally(function() {
|
||||
showLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************************************
|
||||
//* Filter-mod and config-mod actions : *
|
||||
//*******************************************************************
|
||||
//*******************************************************************
|
||||
//* Filter-mod and config-mod actions : *
|
||||
//*******************************************************************
|
||||
|
||||
// Open jail/filter modal and load filter-config
|
||||
function openJailConfigModal(jailName) {
|
||||
function openJailConfigModal(jailName) {
|
||||
currentJailForConfig = jailName;
|
||||
var textArea = document.getElementById('jailConfigTextarea');
|
||||
textArea.value = '';
|
||||
@@ -782,10 +782,9 @@ function openJailConfigModal(jailName) {
|
||||
.finally(function() {
|
||||
showLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Save filter config for the current opened jail
|
||||
function saveJailConfig() {
|
||||
function saveJailConfig() {
|
||||
if (!currentJailForConfig) return;
|
||||
showLoading(true);
|
||||
|
||||
@@ -800,13 +799,10 @@ function saveJailConfig() {
|
||||
if (data.error) {
|
||||
alert("Error saving config: " + data.error);
|
||||
} else {
|
||||
//alert(data.message || "Config saved");
|
||||
console.log("Filter saved successfully. Reload needed? " + data.reloadNeeded);
|
||||
// Hide modal
|
||||
var modalEl = document.getElementById('jailConfigModal');
|
||||
var modalObj = bootstrap.Modal.getInstance(modalEl);
|
||||
modalObj.hide();
|
||||
// Show the reload banner
|
||||
document.getElementById('reloadBanner').style.display = 'block';
|
||||
}
|
||||
})
|
||||
@@ -816,28 +812,27 @@ function saveJailConfig() {
|
||||
.finally(function() {
|
||||
showLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Load current settings when opening settings page
|
||||
function loadSettings() {
|
||||
//*******************************************************************
|
||||
//* Load current settings when opening settings page : *
|
||||
//*******************************************************************
|
||||
|
||||
function loadSettings() {
|
||||
showLoading(true);
|
||||
fetch('/api/settings')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
// Get current general settings
|
||||
document.getElementById('languageSelect').value = data.language || 'en';
|
||||
document.getElementById('debugMode').checked = data.debug || false;
|
||||
|
||||
// Get Alert settings
|
||||
document.getElementById('destEmail').value = data.destemail || '';
|
||||
|
||||
// Get Alert countries selection
|
||||
const select = document.getElementById('alertCountries');
|
||||
for (let i = 0; i < select.options.length; i++) {
|
||||
select.options[i].selected = false;
|
||||
}
|
||||
if (!data.alertCountries || data.alertCountries.length === 0) {
|
||||
// default to "ALL"
|
||||
select.options[0].selected = true;
|
||||
} else {
|
||||
for (let i = 0; i < select.options.length; i++) {
|
||||
@@ -847,10 +842,8 @@ function loadSettings() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update Select2 UI
|
||||
$('#alertCountries').trigger('change');
|
||||
|
||||
// Get SMTP settings
|
||||
if (data.smtp) {
|
||||
document.getElementById('smtpHost').value = data.smtp.host || '';
|
||||
document.getElementById('smtpPort').value = data.smtp.port || 587;
|
||||
@@ -860,7 +853,6 @@ function loadSettings() {
|
||||
document.getElementById('smtpUseTLS').checked = data.smtp.useTLS || false;
|
||||
}
|
||||
|
||||
// Get current Fail2Ban settings
|
||||
document.getElementById('bantimeIncrement').checked = data.bantimeIncrement || false;
|
||||
document.getElementById('banTime').value = data.bantime || '';
|
||||
document.getElementById('findTime').value = data.findtime || '';
|
||||
@@ -871,12 +863,14 @@ function loadSettings() {
|
||||
alert('Error loading settings: ' + err);
|
||||
})
|
||||
.finally(() => showLoading(false));
|
||||
}
|
||||
}
|
||||
|
||||
// Save settings when hitting the save button
|
||||
function saveSettings(event) {
|
||||
event.preventDefault(); // prevent form submission
|
||||
//*******************************************************************
|
||||
//* Save settings when hitting the save button : *
|
||||
//*******************************************************************
|
||||
|
||||
function saveSettings(event) {
|
||||
event.preventDefault();
|
||||
showLoading(true);
|
||||
|
||||
const smtpSettings = {
|
||||
@@ -895,15 +889,12 @@ function saveSettings(event) {
|
||||
debug: document.getElementById('debugMode').checked,
|
||||
destemail: document.getElementById('destEmail').value.trim(),
|
||||
alertCountries: selectedCountries.length > 0 ? selectedCountries : ["ALL"],
|
||||
|
||||
bantimeIncrement: document.getElementById('bantimeIncrement').checked,
|
||||
bantime: document.getElementById('banTime').value.trim(),
|
||||
findtime: document.getElementById('findTime').value.trim(),
|
||||
|
||||
maxretry: parseInt(document.getElementById('maxRetry').value, 10) || 3,
|
||||
ignoreip: document.getElementById('ignoreIP').value.trim(),
|
||||
|
||||
smtp: smtpSettings // (Includes SMTP settings)
|
||||
smtp: smtpSettings
|
||||
};
|
||||
|
||||
fetch('/api/settings', {
|
||||
@@ -916,6 +907,8 @@ function saveSettings(event) {
|
||||
if (data.error) {
|
||||
alert('Error saving settings: ' + data.error + data.details);
|
||||
} else {
|
||||
var selectedLang = $('#languageSelect').val();
|
||||
loadTranslations(selectedLang);
|
||||
console.log("Settings saved successfully. Reload needed? " + data.reloadNeeded);
|
||||
if (data.reloadNeeded) {
|
||||
document.getElementById('reloadBanner').style.display = 'block';
|
||||
@@ -924,10 +917,13 @@ function saveSettings(event) {
|
||||
})
|
||||
.catch(err => alert('Error: ' + err))
|
||||
.finally(() => showLoading(false));
|
||||
}
|
||||
}
|
||||
|
||||
// Load the list of filters from /api/filters
|
||||
function loadFilters() {
|
||||
//*******************************************************************
|
||||
//* Load the list of filters from /api/filters : *
|
||||
//*******************************************************************
|
||||
|
||||
function loadFilters() {
|
||||
showLoading(true);
|
||||
fetch('/api/filters')
|
||||
.then(res => res.json())
|
||||
@@ -937,7 +933,7 @@ function loadFilters() {
|
||||
return;
|
||||
}
|
||||
const select = document.getElementById('filterSelect');
|
||||
select.innerHTML = ''; // clear existing
|
||||
select.innerHTML = '';
|
||||
if (!data.filters || data.filters.length === 0) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = '';
|
||||
@@ -956,9 +952,9 @@ function loadFilters() {
|
||||
alert('Error loading filters: ' + err);
|
||||
})
|
||||
.finally(() => showLoading(false));
|
||||
}
|
||||
}
|
||||
|
||||
function sendTestEmail() {
|
||||
function sendTestEmail() {
|
||||
showLoading(true);
|
||||
|
||||
fetch('/api/settings/test-email', {
|
||||
@@ -975,10 +971,10 @@ function sendTestEmail() {
|
||||
})
|
||||
.catch(error => alert('Error: ' + error))
|
||||
.finally(() => showLoading(false));
|
||||
}
|
||||
}
|
||||
|
||||
// Called when clicking "Test Filter" button
|
||||
function testSelectedFilter() {
|
||||
// Called when clicking "Test Filter" button
|
||||
function testSelectedFilter() {
|
||||
const filterName = document.getElementById('filterSelect').value;
|
||||
const lines = document.getElementById('logLinesTextarea').value.split('\n');
|
||||
|
||||
@@ -1008,12 +1004,12 @@ function testSelectedFilter() {
|
||||
alert('Error: ' + err);
|
||||
})
|
||||
.finally(() => showLoading(false));
|
||||
}
|
||||
}
|
||||
|
||||
function renderTestResults(matches) {
|
||||
let html = '<h5>Test Results</h5>';
|
||||
function renderTestResults(matches) {
|
||||
let html = '<h5 data-i18n="filter_debug.test_results_title">Test Results</h5>';
|
||||
if (!matches || matches.length === 0) {
|
||||
html += '<p>No matches found.</p>';
|
||||
html += '<p data-i18n="filter_debug.no_matches">No matches found.</p>';
|
||||
} else {
|
||||
html += '<ul>';
|
||||
matches.forEach(m => {
|
||||
@@ -1022,21 +1018,20 @@ function renderTestResults(matches) {
|
||||
html += '</ul>';
|
||||
}
|
||||
document.getElementById('testResults').innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
// When showing the filter section
|
||||
function showFilterSection() {
|
||||
loadFilters(); // fetch the filter list
|
||||
// When showing the filter section
|
||||
function showFilterSection() {
|
||||
loadFilters();
|
||||
document.getElementById('testResults').innerHTML = '';
|
||||
document.getElementById('logLinesTextarea').value = '';
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************************************
|
||||
//* Reload fail2ban action : *
|
||||
//*******************************************************************
|
||||
//*******************************************************************
|
||||
//* Reload fail2ban action : *
|
||||
//*******************************************************************
|
||||
|
||||
// Reload Fail2ban
|
||||
function reloadFail2ban() {
|
||||
function reloadFail2ban() {
|
||||
if (!confirm("It can happen that some logs are not parsed during the reload of fail2ban. Reload Fail2ban now?")) return;
|
||||
showLoading(true);
|
||||
fetch('/api/fail2ban/reload', { method: 'POST' })
|
||||
@@ -1045,9 +1040,7 @@ function reloadFail2ban() {
|
||||
if (data.error) {
|
||||
alert("Error: " + data.error);
|
||||
} else {
|
||||
// Hide reload banner
|
||||
document.getElementById('reloadBanner').style.display = 'none';
|
||||
// Refresh data
|
||||
return fetchSummary();
|
||||
}
|
||||
})
|
||||
@@ -1057,21 +1050,19 @@ function reloadFail2ban() {
|
||||
.finally(function() {
|
||||
showLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************************************
|
||||
//* Is executed when doc-ready : *
|
||||
//*******************************************************************
|
||||
//*******************************************************************
|
||||
//* Is executed when doc-ready : *
|
||||
//*******************************************************************
|
||||
|
||||
$(document).ready(function() {
|
||||
// Initialize the Select2 widget with a placeholder and full width.
|
||||
$('#alertCountries').select2({
|
||||
placeholder: 'Select countries..',
|
||||
allowClear: true,
|
||||
width: '100%'
|
||||
});
|
||||
|
||||
// Custom logic: If "ALL" is selected, remove other selections.
|
||||
$('#alertCountries').on('select2:select', function(e) {
|
||||
var selectedValue = e.params.data.id;
|
||||
var currentValues = $('#alertCountries').val() || [];
|
||||
@@ -1089,6 +1080,57 @@ function reloadFail2ban() {
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
//*******************************************************************
|
||||
//* Translation Related Functions : *
|
||||
//*******************************************************************
|
||||
var translations = {};
|
||||
|
||||
// Loads translation JSON file for given language (e.g., en, de, etc.)
|
||||
function loadTranslations(lang) {
|
||||
$.getJSON('/locales/' + lang + '.json')
|
||||
.done(function(data) {
|
||||
translations = data;
|
||||
updateTranslations();
|
||||
})
|
||||
.fail(function() {
|
||||
console.error('Failed to load translations for language:', lang);
|
||||
});
|
||||
}
|
||||
|
||||
// Updates all elements with data-i18n attribute with corresponding translation.
|
||||
function updateTranslations() {
|
||||
$('[data-i18n]').each(function() {
|
||||
var key = $(this).data('i18n');
|
||||
if (translations[key]) {
|
||||
$(this).text(translations[key]);
|
||||
}
|
||||
});
|
||||
// Updates placeholders.
|
||||
$('[data-i18n-placeholder]').each(function() {
|
||||
var key = $(this).data('i18n-placeholder');
|
||||
if (translations[key]) {
|
||||
$(this).attr('placeholder', translations[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getTranslationsSettingsOnPageload() {
|
||||
// Fetch settings to get the current language preference
|
||||
fetch('/api/settings')
|
||||
.then(function(res) { return res.json(); })
|
||||
.then(function(data) {
|
||||
var lang = data.language || 'en'; // Use the language from settings or default to "en"
|
||||
$('#languageSelect').val(lang); // Update the language dropdown accordingly
|
||||
loadTranslations(lang); // Load the appropriate translation file
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Error loading initial settings:', err);
|
||||
// In case of an error, fallback to English
|
||||
loadTranslations('en');
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user