mirror of
https://github.com/gosticks/web.git
synced 2026-10-05 14:16:57 +00:00
Merge pull request #115 from pi-hole/feature/save-dns-settings
DNS Settings Page
This commit is contained in:
+1
-1
@@ -351,7 +351,7 @@ function getDNSInfo() {
|
||||
fqdn_required: faker.random.boolean(),
|
||||
bogus_priv: faker.random.boolean(),
|
||||
dnssec: faker.random.boolean(),
|
||||
listening_type: faker.random.arrayElement(["single", "lan", "all"])
|
||||
listening_type: faker.random.arrayElement(["single", "local", "all"])
|
||||
},
|
||||
conditional_forwarding: {
|
||||
enabled: faker.random.boolean(),
|
||||
|
||||
Generated
+109
-8
@@ -4004,6 +4004,15 @@
|
||||
"sha.js": "^2.4.8"
|
||||
}
|
||||
},
|
||||
"create-react-context": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.2.tgz",
|
||||
"integrity": "sha512-KkpaLARMhsTsgp0d2NA/R94F/eDLbhXERdIq3LvX2biCAXcDvHYoOqHfWCHf1+OLj+HKBotLG3KqaOOf+C1C+A==",
|
||||
"requires": {
|
||||
"fbjs": "^0.8.0",
|
||||
"gud": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
|
||||
@@ -5252,8 +5261,7 @@
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"escodegen": {
|
||||
"version": "1.9.1",
|
||||
@@ -7224,6 +7232,11 @@
|
||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
|
||||
"dev": true
|
||||
},
|
||||
"gud": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
||||
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
|
||||
},
|
||||
"gzip-size": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz",
|
||||
@@ -11317,10 +11330,9 @@
|
||||
}
|
||||
},
|
||||
"merge": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz",
|
||||
"integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=",
|
||||
"dev": true
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
|
||||
"integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ=="
|
||||
},
|
||||
"merge-deep": {
|
||||
"version": "3.0.2",
|
||||
@@ -17753,6 +17765,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"prop-types-extra": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz",
|
||||
"integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==",
|
||||
"requires": {
|
||||
"react-is": "^16.3.2",
|
||||
"warning": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
|
||||
@@ -18034,6 +18055,64 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-bootstrap-typeahead": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/react-bootstrap-typeahead/-/react-bootstrap-typeahead-3.2.4.tgz",
|
||||
"integrity": "sha512-wFXIKbOHY4Q1AtnhSQIAnUvSDxmcFdDc5wIX/yd7YJug2jL5TvKspJ3XfX/FaWEWyn1pgHukRuFGnbYnL8rtUQ==",
|
||||
"requires": {
|
||||
"classnames": "^2.2.0",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"invariant": "^2.2.1",
|
||||
"lodash": "^4.17.2",
|
||||
"prop-types": "^15.5.8",
|
||||
"prop-types-extra": "^1.0.1",
|
||||
"react-onclickoutside": "^6.1.1",
|
||||
"react-overlays": "^0.8.1",
|
||||
"react-popper": "^1.0.0",
|
||||
"warning": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz",
|
||||
"integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.12.0"
|
||||
}
|
||||
},
|
||||
"popper.js": {
|
||||
"version": "1.14.6",
|
||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.6.tgz",
|
||||
"integrity": "sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA=="
|
||||
},
|
||||
"react-popper": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.2.tgz",
|
||||
"integrity": "sha512-UbFWj55Yt9uqvy0oZ+vULDL2Bw1oxeZF9/JzGyxQ5ypgauRH/XlarA5+HLZWro/Zss6Ht2kqpegtb6sYL8GUGw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"create-react-context": "<=0.2.2",
|
||||
"popper.js": "^1.14.4",
|
||||
"prop-types": "^15.6.1",
|
||||
"typed-styles": "^0.0.7",
|
||||
"warning": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
|
||||
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
|
||||
},
|
||||
"warning": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz",
|
||||
"integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-chartjs-2": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-2.7.2.tgz",
|
||||
@@ -18294,8 +18373,25 @@
|
||||
"react-is": {
|
||||
"version": "16.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.1.tgz",
|
||||
"integrity": "sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ=="
|
||||
},
|
||||
"react-onclickoutside": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.7.1.tgz",
|
||||
"integrity": "sha512-p84kBqGaMoa7VYT0vZ/aOYRfJB+gw34yjpda1Z5KeLflg70HipZOT+MXQenEhdkPAABuE2Astq4zEPdMqUQxcg=="
|
||||
},
|
||||
"react-overlays": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.8.3.tgz",
|
||||
"integrity": "sha512-h6GT3jgy90PgctleP39Yu3eK1v9vaJAW73GOA/UbN9dJ7aAN4BTZD6793eI1D5U+ukMk17qiqN/wl3diK1Z5LA==",
|
||||
"requires": {
|
||||
"classnames": "^2.2.5",
|
||||
"dom-helpers": "^3.2.1",
|
||||
"prop-types": "^15.5.10",
|
||||
"prop-types-extra": "^1.0.1",
|
||||
"react-transition-group": "^2.2.0",
|
||||
"warning": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"react-perfect-scrollbar": {
|
||||
"version": "1.1.1",
|
||||
@@ -21286,6 +21382,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"typed-styles": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
|
||||
"integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
|
||||
},
|
||||
"typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
|
||||
+4
-3
@@ -25,14 +25,14 @@
|
||||
"@coreui/react": "^2.0.4",
|
||||
"bootstrap": "^4.1.1",
|
||||
"chart.js": "^2.7.2",
|
||||
"font-awesome": "^4.7.0",
|
||||
"i18next": "^11.3.2",
|
||||
"i18next-browser-languagedetector": "^2.2.0",
|
||||
"i18next-xhr-backend": "^1.5.1",
|
||||
"ionicons": "^3.0.0",
|
||||
"font-awesome": "^4.7.0",
|
||||
"simple-line-icons": "^2.4.1",
|
||||
"prop-types": "^15.6.1",
|
||||
"react": "^16.4.1",
|
||||
"react-bootstrap-typeahead": "^3.2.4",
|
||||
"react-chartjs-2": "^2.7.2",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-i18next": "^7.6.1",
|
||||
@@ -40,7 +40,8 @@
|
||||
"react-table": "^6.8.2",
|
||||
"react-transition-group": "^2.3.1",
|
||||
"reactstrap": "^6.1.0",
|
||||
"sha.js": "^2.4.11"
|
||||
"sha.js": "^2.4.11",
|
||||
"simple-line-icons": "^2.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run build-css && npm-run-all -p watch-css start-js",
|
||||
|
||||
@@ -25,5 +25,7 @@
|
||||
"Only forward public reverse lookups": "Only forward public reverse lookups",
|
||||
"Processing...": "Processing...",
|
||||
"Successfully saved settings": "Successfully saved settings",
|
||||
"Apply": "Apply"
|
||||
"Apply": "Apply",
|
||||
"Detected custom upstream server": "Detected custom upstream server",
|
||||
"DNS Options": "DNS Options"
|
||||
}
|
||||
@@ -1 +1,22 @@
|
||||
{}
|
||||
{
|
||||
"unknown": "desconocido",
|
||||
"gravity_error": "Error_Gravity",
|
||||
"ftl_connection_fail": "Fallo_Conexión_FTL",
|
||||
"ftl_read_error": "Error_lectura_FTL",
|
||||
"ftl_eom_error": "Error_eom_FTL",
|
||||
"not_found": "No_se_encuentra",
|
||||
"already_exists": "Ya_existe",
|
||||
"invalid_domain": "Domino_inválido",
|
||||
"bad_request": "Petición_inválida",
|
||||
"unauthorized": "Sin_autorización",
|
||||
"file_read": "Archivo_leido",
|
||||
"file_write": "Archivo_escrito",
|
||||
"config_parsing_error": "Error_en_la_configuración",
|
||||
"invalid_setting_value": "Valor_inválido",
|
||||
"restart_dns_error": "Error_reinicio_DNS",
|
||||
"dnsmasq_config_write": "Escritura_config_dnsmasq",
|
||||
"shared_memory_open": "shared_memory_open",
|
||||
"shared_memory_read": "shared_memory_read",
|
||||
"shared_memory_lock": "shared_memory_lock",
|
||||
"API Error: {{error}}": "API Error: {{error}}"
|
||||
}
|
||||
@@ -22,5 +22,8 @@
|
||||
"Branch": "Rama",
|
||||
"Hash": "Hash",
|
||||
"Tag": "Etiqueta",
|
||||
"Only forward public reverse lookups": "Solo búsquedas públicas inversas hacia adelante"
|
||||
"Only forward public reverse lookups": "Solo búsquedas públicas inversas hacia adelante",
|
||||
"Processing...": "Procesando...",
|
||||
"Successfully saved settings": "Opciones salvadas con exito",
|
||||
"Apply": "Aplicar"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"unknown": "Errore sconosciuto",
|
||||
"gravity_error": "Errore nella creazione della lista dei bloccati",
|
||||
"ftl_connection_fail": "Errore nella connessione a FTL",
|
||||
"ftl_read_error": "Errore nella lettura da FTL",
|
||||
"ftl_eom_error": "Lettura EOM inaspettata da FTL",
|
||||
"not_found": "Non trovato",
|
||||
"already_exists": "L'oggetto esiste già",
|
||||
"invalid_domain": "Dominio non valido",
|
||||
"bad_request": "Richiesta non valida",
|
||||
"unauthorized": "Non hai le autorizzazioni necessarie",
|
||||
"file_read": "Errore nella lettura da {{file}}",
|
||||
"file_write": "Errore nella scrittura su {{file}}",
|
||||
"config_parsing_error": "Errore nell'operazione di parsing della configurazione",
|
||||
"invalid_setting_value": "Valore del parametro non valido",
|
||||
"restart_dns_error": "Errore nel riavvio del server DNS",
|
||||
"dnsmasq_config_write": "Errore nella generazione della configurazione di dnsmasq",
|
||||
"shared_memory_open": "Errore nell'apertura della memoria condivisa",
|
||||
"shared_memory_read": "Errore nella lettura della memoria condivisa",
|
||||
"shared_memory_lock": "Errore nel blocco della memoria condivisa",
|
||||
"API Error: {{error}}": "Errore dell'API: {{error}}"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"Details": "Dettagli\n",
|
||||
"Status": "Stato",
|
||||
"DNS": "DNS",
|
||||
"Donate": "Dona",
|
||||
"Domain": "Dominî",
|
||||
"Hits": "Nº richieste",
|
||||
"Frequency": "Frequenza",
|
||||
"Client": "Client",
|
||||
"Requests": "Richieste",
|
||||
"Add": "Aggiungi",
|
||||
"Time": "Data",
|
||||
"Type": "Tipo",
|
||||
"Network": "Rete",
|
||||
"Action": "Azione",
|
||||
"Enabled": "Attivato",
|
||||
"Disabled": "Disattivato",
|
||||
"DHCP": "DHCP",
|
||||
"Core": "Core",
|
||||
"FTL": "FTL",
|
||||
"API": "API",
|
||||
"Web": "Web",
|
||||
"Version": "Versione"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"Domains On Blocklist": "Dominî nella lista dei bloccati",
|
||||
"Query Types": "Tipi di interrogazioni",
|
||||
"Forward Destinations": "Destinazioni inviate",
|
||||
"Top Permitted Domains": "Classifica dei dominî consentiti più richiesti",
|
||||
"Top Blocked Domains": "Classifica dei dominî bloccati più richiesti",
|
||||
"Top Clients": "Classifica dei client",
|
||||
"Total Queries ({{count}} clients)": "Totale delle interrogazioni (tramite 1 client)",
|
||||
"Total Queries ({{count}} clients)_plural": "Totale delle interrogazioni (tramite {{count}} client)",
|
||||
"Queries Blocked": "Interrogazioni bloccate",
|
||||
"Percent Blocked": "Percentuale bloccata",
|
||||
"Queries Over Last 24 Hours": "Le interrogazioni effettuate nelle ultime 24 ore",
|
||||
"Clients Over Last 24 Hours": "I client che hanno effettuato richieste nelle ultime 24 ore",
|
||||
"Queries Answered By": "Interrogazioni fornite da",
|
||||
"Queries from {{from}} to {{to}}": "Interrogazioni dalle {{from}} alle {{to}}",
|
||||
"Total Queries": "Totale delle interrogazioni",
|
||||
"Blocked Queries": "Interrogazioni bloccate",
|
||||
"Client activity from {{from}} to {{to}}": "Attività del client dalle {{from}} alle {{to}}",
|
||||
"{{percent}}% of {{total}}": "{{percent}}% di {{total}}",
|
||||
"No Clients Found": "Nessun client trovato",
|
||||
"No Domains Found": "Nessun dominio trovato",
|
||||
"Queries Answered By Destination": "Interrogazioni fornite dalla destinazione"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"if you found this useful": "se ritieni questo software utile",
|
||||
"Core Version": "Versione Pi-hole",
|
||||
"Web Version": "Versione interfaccia web",
|
||||
"Update Available": "È disponibile un aggiornamento"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"Adding {{domain}}...": "Aggiunta di {{domain}} in corso...",
|
||||
"Successfully added {{domain}}": "{{domain}} è stato aggiunto alla lista",
|
||||
"Failed to add {{domain}}": "Errore nell'aggiunta di {{domain}}",
|
||||
"Note: Whitelisting a subdomain of a wildcard blocked domain is not possible.": "Nota: inserire nella lista bianca un sottodominio di un dominio già bloccato da wildcard non è possibile.",
|
||||
"Note: Only the domain and subdomains of the blocked domain will be blocked.": "Nota: solo il dominio e i suoi sottodominî saranno bloccati.",
|
||||
"Add a domain (example.com or sub.example.com)": "Aggiungi un dominio (come example.com o sub.example.com)",
|
||||
"{{domain}} is already added": "{{domain}} è già stato aggiunto",
|
||||
"Failed to remove {{domain}}": "Errore nella rimozione di {{domain}}",
|
||||
"There are no domains in this list": "Non ci sono dominî in questa lista",
|
||||
"Input a regular expression": "Inserisci un'espressione regolare",
|
||||
"Not a valid domain": "Non è un dominio valido",
|
||||
"Not a valid regular expression": "Non è un'espressione regolare valida"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"Query Log": "Registro delle interrogazioni",
|
||||
"Whitelist": "Lista bianca",
|
||||
"Blacklist": "Lista nera",
|
||||
"Dashboard": "Pannello delle statistiche",
|
||||
"Exact": "L'esatto",
|
||||
"Wildcard": "Wildcard",
|
||||
"Login": "Accedi",
|
||||
"Logout": "Esci",
|
||||
"Regex": "Espressione regolare",
|
||||
"Settings": "Impostazioni",
|
||||
"Networking": "Networking",
|
||||
"Versions": "Versioni"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"Password": "Password",
|
||||
"Sign in to start your session": "Accedi per avviare la sessione",
|
||||
"Log in": "Accedi",
|
||||
"Forgot Password": "Hai dimenticato la password?",
|
||||
"forgot_password_description": "Una volta configurato Pi-hole per la prima volta, viene generata e mostrata una password all'utente. La password non può essere più recuperata in futuro, ma è possibile impostare una nuova password digitando questo comando (la password può essere disattivata se si lascia il campo vuoto):",
|
||||
"Wrong Password!": "Password errata!",
|
||||
"You will be transferred to the \"{{page}}\" page": "Verrai reindirizzato alla pagina «{{page}}»",
|
||||
"Verify that cookies are allowed for {{host}}": "Verifica che i cookie sono consentiti per {{host}}"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"Blocked": "Bloccato",
|
||||
"Allowed (forwarded)": "Permesso (inviato)",
|
||||
"Allowed (cached)": "Permesso (dalla cache)",
|
||||
"Blocked (regex)": "Bloccato (da espressione regolare)",
|
||||
"Blocked (blacklist)": "Bloccato (dalla lista nera)"
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"DHCP Active": "DHCP attivo",
|
||||
"Start IP": "IP iniziale",
|
||||
"End IP": "IP finale",
|
||||
"Router IP": "IP del router",
|
||||
"Lease Time": "Tempo del lease",
|
||||
"IPv6 Support": "Supporto a IPv6",
|
||||
"Upstream DNS Servers": "Server DNS di Upstream",
|
||||
"Interface listening behavior": "Comportamento dell'interfaccia di ascolto",
|
||||
"Forward FQDNs only": "Invia solo FQDNs",
|
||||
"Use DNSSEC": "Utilizza DNSSEC",
|
||||
"Conditional Forwarding": "Invio condizionale",
|
||||
"Local Domain Name": "Nome del dominio locale",
|
||||
"FTL Database": "Database FTL",
|
||||
"Queries": "Interrogazioni",
|
||||
"Filesize": "Dimensioni del file",
|
||||
"SQLite version": "Versione di SQLite",
|
||||
"Interface": "Interfaccia",
|
||||
"IPv4 address": "Indirizzo IPv4",
|
||||
"IPv6 address": "Indirizzo IPv6",
|
||||
"Hostname": "Nome dell'host",
|
||||
"Branch": "Branch",
|
||||
"Hash": "Hash",
|
||||
"Tag": "Etichetta",
|
||||
"Only forward public reverse lookups": "Invia solo i lookup pubblici invertiti",
|
||||
"Processing...": "Elaborazione in corso...",
|
||||
"Successfully saved settings": "Impostazioni salvate con successo",
|
||||
"Apply": "Applica"
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* Web Interface
|
||||
* Settings :: DNS :: Upstream DNS Servers
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Col, FormGroup, Input, Label } from "reactstrap";
|
||||
|
||||
const ConditionalForwardingSettings = ({
|
||||
settings,
|
||||
onUpdate,
|
||||
isRouterIpValid,
|
||||
isDomainValid,
|
||||
t
|
||||
}) => (
|
||||
<Fragment>
|
||||
<FormGroup check>
|
||||
<Label check>
|
||||
<Input
|
||||
type="checkbox"
|
||||
checked={settings.enabled}
|
||||
onChange={e => onUpdate({ ...settings, enabled: e.target.checked })}
|
||||
/>
|
||||
{t("Enabled")}
|
||||
</Label>
|
||||
</FormGroup>
|
||||
<FormGroup row>
|
||||
<Label for="routerIP" sm={5}>
|
||||
{t("Router IP")}
|
||||
</Label>
|
||||
<Col sm={7}>
|
||||
<Input
|
||||
id="routerIP"
|
||||
disabled={!settings.enabled}
|
||||
value={settings.routerIp}
|
||||
onChange={e => onUpdate({ ...settings, routerIp: e.target.value })}
|
||||
invalid={!isRouterIpValid}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
<FormGroup row>
|
||||
<Label for="localDomain" sm={5}>
|
||||
{t("Local Domain Name")}
|
||||
</Label>
|
||||
<Col sm={7}>
|
||||
<Input
|
||||
id="localDomain"
|
||||
disabled={!settings.enabled}
|
||||
value={settings.domain}
|
||||
onChange={e => onUpdate({ ...settings, domain: e.target.value })}
|
||||
invalid={!isDomainValid}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
ConditionalForwardingSettings.propTypes = {
|
||||
settings: PropTypes.object.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
isRouterIpValid: PropTypes.bool.isRequired,
|
||||
isDomainValid: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ConditionalForwardingSettings;
|
||||
@@ -3,7 +3,7 @@
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* Web Interface
|
||||
* Settings :: DHCP
|
||||
* Settings :: DNS
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
@@ -12,93 +12,219 @@ import React, { Component } from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { ignoreCancel, makeCancelable } from "../../util";
|
||||
import api from "../../util/api";
|
||||
import DnsList from "./DnsList";
|
||||
import { Button, Col, Form, FormGroup } from "reactstrap";
|
||||
import ConditionalForwardingSettings from "./ConditionalForwardingSettings";
|
||||
import DnsOptionSettings from "./DnsOptionSettings";
|
||||
import Alert from "../common/Alert";
|
||||
import { isValidHostname, isValidIpv4 } from "../../validate";
|
||||
|
||||
class DNSInfo extends Component {
|
||||
state = {
|
||||
upstream_dns: [],
|
||||
conditional_forwarding: {
|
||||
alertMessage: "",
|
||||
alertType: "",
|
||||
showAlert: false,
|
||||
processing: false,
|
||||
upstreamDns: [],
|
||||
conditionalForwarding: {
|
||||
enabled: false,
|
||||
router_ip: "",
|
||||
routerIp: "",
|
||||
domain: ""
|
||||
},
|
||||
options: {
|
||||
fqdn_required: false,
|
||||
bogus_priv: false,
|
||||
fqdnRequired: false,
|
||||
bogusPriv: false,
|
||||
dnssec: false,
|
||||
listening_type: "single"
|
||||
listeningType: "single"
|
||||
}
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.updateDNSInfo = this.updateDNSInfo.bind(this);
|
||||
}
|
||||
|
||||
updateDNSInfo() {
|
||||
this.updateHandler = makeCancelable(api.getDNSInfo(), {
|
||||
repeat: this.updateDNSInfo,
|
||||
interval: 600000
|
||||
});
|
||||
this.updateHandler.promise
|
||||
loadDNSInfo = () => {
|
||||
this.loadHandler = makeCancelable(api.getDNSInfo());
|
||||
this.loadHandler.promise
|
||||
.then(res => {
|
||||
this.setState(res);
|
||||
// If the domain is empty, fill it in with a default.
|
||||
const savedDomain = res.conditional_forwarding.domain;
|
||||
const domain = savedDomain.length === 0 ? "lan" : savedDomain;
|
||||
|
||||
this.setState({
|
||||
upstreamDns: res.upstream_dns,
|
||||
conditionalForwarding: {
|
||||
enabled: res.conditional_forwarding.enabled,
|
||||
routerIp: res.conditional_forwarding.router_ip,
|
||||
domain
|
||||
},
|
||||
options: {
|
||||
fqdnRequired: res.options.fqdn_required,
|
||||
bogusPriv: res.options.bogus_priv,
|
||||
dnssec: res.options.dnssec,
|
||||
listeningType: res.options.listening_type
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(ignoreCancel);
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.updateDNSInfo();
|
||||
this.loadDNSInfo();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.updateHandler.cancel();
|
||||
this.loadHandler.cancel();
|
||||
}
|
||||
|
||||
handleUpstreamAdd = upstream => {
|
||||
this.setState({
|
||||
upstreamDns: this.state.upstreamDns.concat(upstream)
|
||||
});
|
||||
};
|
||||
|
||||
handleUpstreamRemove = upstream => {
|
||||
this.setState({
|
||||
upstreamDns: this.state.upstreamDns.filter(item => item !== upstream)
|
||||
});
|
||||
};
|
||||
|
||||
handleConditionalForwardingUpdate = conditionalForwarding => {
|
||||
this.setState({ conditionalForwarding });
|
||||
};
|
||||
|
||||
handleDnsOptionsUpdate = options => {
|
||||
this.setState({ options });
|
||||
};
|
||||
|
||||
/**
|
||||
* Save changes to DNS settings
|
||||
*
|
||||
* @param e the submit event
|
||||
*/
|
||||
saveSettings = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const { t } = this.props;
|
||||
|
||||
this.setState({
|
||||
alertMessage: t("Processing..."),
|
||||
alertType: "info",
|
||||
showAlert: true,
|
||||
processing: true
|
||||
});
|
||||
|
||||
this.updateHandler = makeCancelable(
|
||||
api.updateDNSInfo({
|
||||
upstream_dns: this.state.upstreamDns,
|
||||
conditional_forwarding: {
|
||||
enabled: this.state.conditionalForwarding.enabled,
|
||||
router_ip: this.state.conditionalForwarding.routerIp,
|
||||
domain: this.state.conditionalForwarding.domain
|
||||
},
|
||||
options: {
|
||||
fqdn_required: this.state.options.fqdnRequired,
|
||||
bogus_priv: this.state.options.bogusPriv,
|
||||
dnssec: this.state.options.dnssec,
|
||||
listening_type: this.state.options.listeningType
|
||||
}
|
||||
})
|
||||
);
|
||||
this.updateHandler.promise
|
||||
.then(() => {
|
||||
this.setState({
|
||||
alertMessage: t("Successfully saved settings"),
|
||||
alertType: "success",
|
||||
showAlert: true,
|
||||
processing: false
|
||||
});
|
||||
})
|
||||
.catch(ignoreCancel)
|
||||
.catch(error => {
|
||||
let message = "";
|
||||
|
||||
if (error instanceof Error) {
|
||||
message = error.message;
|
||||
} else {
|
||||
// Translate the API's error message
|
||||
message = t("API Error: {{error}}", {
|
||||
error: t(error.key, error.data)
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
alertMessage: message,
|
||||
alertType: "danger",
|
||||
showAlert: true,
|
||||
processing: false
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
isCFSettingValid = (value, validator) => {
|
||||
return (
|
||||
(!this.state.conditionalForwarding.enabled && value.length === 0) ||
|
||||
validator(value)
|
||||
);
|
||||
};
|
||||
|
||||
hideAlert = () => {
|
||||
this.setState({ showAlert: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
const isRouterIpValid = this.isCFSettingValid(
|
||||
this.state.conditionalForwarding.routerIp,
|
||||
isValidIpv4
|
||||
);
|
||||
|
||||
const isDomainValid = this.isCFSettingValid(
|
||||
this.state.conditionalForwarding.domain,
|
||||
isValidHostname
|
||||
);
|
||||
|
||||
const alert = this.state.showAlert ? (
|
||||
<Alert
|
||||
message={this.state.alertMessage}
|
||||
type={this.state.alertType}
|
||||
onClick={this.hideAlert}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-lg-4 col-sm-4 col-xs-4">
|
||||
<pre>
|
||||
<br />
|
||||
{t("Upstream DNS Servers")}:<br />
|
||||
{this.state.upstream_dns.map(item => item + "\n")}
|
||||
<br />
|
||||
</pre>
|
||||
</div>
|
||||
<div className="col-lg-4 col-sm-4 col-xs-4">
|
||||
<pre>
|
||||
<br />
|
||||
{t("Interface listening behavior")}:{" "}
|
||||
{this.state.options.listening_type}
|
||||
<br />
|
||||
{t("Forward FQDNs only")}:{" "}
|
||||
{this.state.options.fqdn_required.toString()}
|
||||
<br />
|
||||
{t("Only forward public reverse lookups")}:
|
||||
{this.state.options.bogus_priv.toString()}
|
||||
<br />
|
||||
{t("Use DNSSEC")}: {this.state.options.dnssec.toString()}
|
||||
</pre>
|
||||
</div>
|
||||
<div className="col-lg-4 col-sm-4 col-xs-4">
|
||||
<pre>
|
||||
<br />
|
||||
{t("Conditional Forwarding")}
|
||||
<br />
|
||||
{t("Enabled")}:{" "}
|
||||
{this.state.conditional_forwarding.enabled.toString()}
|
||||
<br />
|
||||
{t("Router IP")}:{" "}
|
||||
{this.state.conditional_forwarding.router_ip.toString()}
|
||||
<br />
|
||||
{t("Local Domain Name")}:{" "}
|
||||
{this.state.conditional_forwarding.domain.toString()}
|
||||
<br />
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<Form onSubmit={this.saveSettings}>
|
||||
{alert}
|
||||
<FormGroup row>
|
||||
<Col sm={6}>
|
||||
<h3>{t("Upstream DNS Servers")}</h3>
|
||||
<DnsList
|
||||
onAdd={this.handleUpstreamAdd}
|
||||
onRemove={this.handleUpstreamRemove}
|
||||
upstreams={this.state.upstreamDns}
|
||||
/>
|
||||
</Col>
|
||||
<Col sm={6}>
|
||||
<h3>{t("Conditional Forwarding")}</h3>
|
||||
<ConditionalForwardingSettings
|
||||
settings={this.state.conditionalForwarding}
|
||||
onUpdate={this.handleConditionalForwardingUpdate}
|
||||
isRouterIpValid={isRouterIpValid}
|
||||
isDomainValid={isDomainValid}
|
||||
t={t}
|
||||
/>
|
||||
<h3>{t("DNS Options")}</h3>
|
||||
<DnsOptionSettings
|
||||
settings={this.state.options}
|
||||
onUpdate={this.handleDnsOptionsUpdate}
|
||||
t={t}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={this.state.processing || !isRouterIpValid || !isDomainValid}
|
||||
>
|
||||
{t("Apply")}
|
||||
</Button>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* Web Interface
|
||||
* An Editable List of Upstream DNS Servers
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { ListGroup } from "reactstrap";
|
||||
import DnsListItem from "./DnsListItem";
|
||||
import DnsListNewItem from "./DnsListNewItem";
|
||||
import { isValidIpv4OptionalPort } from "../../validate";
|
||||
|
||||
/**
|
||||
* Check if an upstream address is unique and valid
|
||||
*
|
||||
* @param address the address to check
|
||||
* @param upstreams the list of current upstreams
|
||||
* @returns {boolean} if the upstream address is unique and valid
|
||||
*/
|
||||
export const isAddressValid = (address, upstreams) =>
|
||||
!upstreams.includes(address) && isValidIpv4OptionalPort(address);
|
||||
|
||||
const DnsList = ({ upstreams, onAdd, onRemove }) => (
|
||||
<ListGroup style={{ overflowY: "scroll", maxHeight: "350px" }}>
|
||||
{upstreams.map(upstream => (
|
||||
<DnsListItem
|
||||
key={upstream}
|
||||
onRemove={() => onRemove(upstream)}
|
||||
address={upstream}
|
||||
/>
|
||||
))}
|
||||
<DnsListNewItem
|
||||
onAdd={onAdd}
|
||||
isValid={address => isAddressValid(address, upstreams)}
|
||||
upstreams={upstreams}
|
||||
/>
|
||||
</ListGroup>
|
||||
);
|
||||
|
||||
DnsList.propTypes = {
|
||||
upstreams: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
onAdd: PropTypes.func.isRequired,
|
||||
onRemove: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default DnsList;
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* Web Interface
|
||||
* A Upstream DNS List Item
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Button, ListGroupItem } from "reactstrap";
|
||||
|
||||
const DnsListItem = ({ address, onRemove }) => (
|
||||
<ListGroupItem>
|
||||
{address}
|
||||
<Button color="danger" size="sm" className="pull-right" onClick={onRemove}>
|
||||
<span className="fa fa-trash-o" />
|
||||
</Button>
|
||||
</ListGroupItem>
|
||||
);
|
||||
|
||||
DnsListItem.propTypes = {
|
||||
address: PropTypes.string.isRequired,
|
||||
onRemove: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default DnsListItem;
|
||||
@@ -0,0 +1,88 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* Web Interface
|
||||
* Input For New DNS List Items
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Button, InputGroup, InputGroupAddon, ListGroupItem } from "reactstrap";
|
||||
import { Typeahead } from "react-bootstrap-typeahead";
|
||||
import { translate } from "react-i18next";
|
||||
import { preconfiguredUpstreamOptions } from "./preconfiguredUpstreams";
|
||||
|
||||
/**
|
||||
* A component to add upstream DNS servers. The servers are either selected from
|
||||
* a list of preconfigured upstreams, or custom servers can be input.
|
||||
*/
|
||||
class DnsListNewItem extends Component {
|
||||
static propTypes = {
|
||||
onAdd: PropTypes.func.isRequired,
|
||||
isValid: PropTypes.func.isRequired,
|
||||
upstreams: PropTypes.arrayOf(PropTypes.string).isRequired
|
||||
};
|
||||
|
||||
state = {
|
||||
address: "",
|
||||
selected: []
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the currently selected upstream address (either custom or preconfigured)
|
||||
*
|
||||
* @returns {string} the selected upstream address
|
||||
*/
|
||||
getAddress = () => {
|
||||
if (this.state.selected.length === 0) {
|
||||
return this.state.address;
|
||||
}
|
||||
|
||||
return this.state.selected[0].address;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
const isAddressValid = this.props.isValid(this.getAddress());
|
||||
|
||||
return (
|
||||
<ListGroupItem>
|
||||
<InputGroup>
|
||||
<Typeahead
|
||||
onInputChange={address => this.setState({ address })}
|
||||
onChange={selected => this.setState({ selected })}
|
||||
options={preconfiguredUpstreamOptions.filter(
|
||||
upstream => !this.props.upstreams.includes(upstream.address)
|
||||
)}
|
||||
selected={this.state.selected}
|
||||
emptyLabel={t("Detected custom upstream server")}
|
||||
bodyContainer
|
||||
/>
|
||||
|
||||
<InputGroupAddon addonType="append">
|
||||
<Button
|
||||
color="success"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
// Add the server to the list
|
||||
this.props.onAdd(this.getAddress());
|
||||
|
||||
// Reset the input
|
||||
this.setState({ address: "", selected: [] });
|
||||
}}
|
||||
disabled={!isAddressValid}
|
||||
>
|
||||
<span className="fa fa-plus" />
|
||||
</Button>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</ListGroupItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("settings")(DnsListNewItem);
|
||||
@@ -0,0 +1,77 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* Web Interface
|
||||
* Settings :: DNS :: DNS Options
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { Col, FormGroup, Input, Label } from "reactstrap";
|
||||
|
||||
const DnsOptionSettings = ({ settings, onUpdate, t }) => (
|
||||
<Fragment>
|
||||
<FormGroup row>
|
||||
<Label for="listeningBehavior" sm={5}>
|
||||
{t("Interface listening behavior")}
|
||||
</Label>
|
||||
<Col sm={7}>
|
||||
<Input
|
||||
id="listeningBehavior"
|
||||
type="select"
|
||||
value={settings.listeningType}
|
||||
onChange={e =>
|
||||
onUpdate({ ...settings, listeningType: e.target.value })
|
||||
}
|
||||
>
|
||||
<option>all</option>
|
||||
<option>local</option>
|
||||
<option>single</option>
|
||||
</Input>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
<FormGroup check>
|
||||
<Label check>
|
||||
<Input
|
||||
type="checkbox"
|
||||
checked={settings.fqdnRequired}
|
||||
onChange={e =>
|
||||
onUpdate({ ...settings, fqdnRequired: e.target.checked })
|
||||
}
|
||||
/>
|
||||
{t("Forward FQDNs only")}
|
||||
</Label>
|
||||
</FormGroup>
|
||||
<FormGroup check>
|
||||
<Label check>
|
||||
<Input
|
||||
type="checkbox"
|
||||
checked={settings.bogusPriv}
|
||||
onChange={e => onUpdate({ ...settings, bogusPriv: e.target.checked })}
|
||||
/>
|
||||
{t("Only forward public reverse lookups")}
|
||||
</Label>
|
||||
</FormGroup>
|
||||
<FormGroup check>
|
||||
<Label check>
|
||||
<Input
|
||||
type="checkbox"
|
||||
checked={settings.dnssec}
|
||||
onChange={e => onUpdate({ ...settings, dnssec: e.target.checked })}
|
||||
/>
|
||||
{t("Use DNSSEC")}
|
||||
</Label>
|
||||
</FormGroup>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
DnsOptionSettings.propTypes = {
|
||||
settings: PropTypes.object.isRequired,
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
t: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default DnsOptionSettings;
|
||||
@@ -0,0 +1,121 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* Web Interface
|
||||
* Preconfigured Upstream DNS Servers
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/**
|
||||
* A list of preconfigured upstream DNS servers. Each item has primary and
|
||||
* secondary IPv4 and IPv6 server entries. Some addresses may be empty.
|
||||
*/
|
||||
export const preconfiguredUpstreams = [
|
||||
{
|
||||
name: "OpenDNS (ECS)",
|
||||
primaryIpv4: "208.67.222.222",
|
||||
secondaryIpv4: "208.67.220.220",
|
||||
primaryIpv6: "2620:0:ccc::2",
|
||||
secondaryIpv6: "2620:0:ccd::2"
|
||||
},
|
||||
{
|
||||
name: "Google (ECS)",
|
||||
primaryIpv4: "8.8.8.8",
|
||||
secondaryIpv4: "8.8.4.4",
|
||||
primaryIpv6: "2001:4860:4860:0:0:0:0:8888",
|
||||
secondaryIpv6: "2001:4860:4860:0:0:0:0:8844"
|
||||
},
|
||||
{
|
||||
name: "Quad9 (filtered, DNSSEC)",
|
||||
primaryIpv4: "9.9.9.9",
|
||||
secondaryIpv4: "149.112.112.112",
|
||||
primaryIpv6: "2620:fe::fe",
|
||||
secondaryIpv6: "2620:fe::9"
|
||||
},
|
||||
{
|
||||
name: "Quad9 (unfiltered, no DNSSEC)",
|
||||
primaryIpv4: "9.9.9.10",
|
||||
secondaryIpv4: "149.112.112.10",
|
||||
primaryIpv6: "2620:fe::10",
|
||||
secondaryIpv6: "2620:fe::fe:10"
|
||||
},
|
||||
{
|
||||
name: "Quad9 (filtered + ECS)",
|
||||
primaryIpv4: "9.9.9.11",
|
||||
secondaryIpv4: "149.112.112.11",
|
||||
primaryIpv6: "2620:fe::11",
|
||||
secondaryIpv6: ""
|
||||
},
|
||||
{
|
||||
name: "Cloudflare (ECS)",
|
||||
primaryIpv4: "1.1.1.1",
|
||||
secondaryIpv4: "1.0.0.1",
|
||||
primaryIpv6: "2606:4700:4700::1111",
|
||||
secondaryIpv6: "2606:4700:4700::1001"
|
||||
},
|
||||
{
|
||||
name: "Level3",
|
||||
primaryIpv4: "4.2.2.1",
|
||||
secondaryIpv4: "4.2.2.2",
|
||||
primaryIpv6: "",
|
||||
secondaryIpv6: ""
|
||||
},
|
||||
{
|
||||
name: "Comodo",
|
||||
primaryIpv4: "8.26.56.26",
|
||||
secondaryIpv4: "8.20.247.20",
|
||||
primaryIpv6: "",
|
||||
secondaryIpv6: ""
|
||||
},
|
||||
{
|
||||
name: "DNS.WATCH",
|
||||
primaryIpv4: "84.200.69.80",
|
||||
secondaryIpv4: "84.200.70.40",
|
||||
primaryIpv6: "2001:1608:10:25:0:0:1c04:b12f",
|
||||
secondaryIpv6: "2001:1608:10:25:0:0:9249:d69b"
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* The preconfigured servers, but with an entry for each address. This is used
|
||||
* when displaying the servers in a list.
|
||||
*/
|
||||
export const preconfiguredUpstreamOptions = preconfiguredUpstreams.flatMap(
|
||||
upstream => {
|
||||
// Parse the upstream address information into the new format
|
||||
const parseUpstream = (address, order, type) => ({
|
||||
label: upstream.name + " " + order + " " + type + " (" + address + ")",
|
||||
address
|
||||
});
|
||||
|
||||
let parsedUpstreams = [];
|
||||
|
||||
if (upstream.primaryIpv4.length > 0) {
|
||||
parsedUpstreams.push(
|
||||
parseUpstream(upstream.primaryIpv4, "Primary", "IPv4")
|
||||
);
|
||||
}
|
||||
|
||||
if (upstream.secondaryIpv4.length > 0) {
|
||||
parsedUpstreams.push(
|
||||
parseUpstream(upstream.secondaryIpv4, "Secondary", "IPv4")
|
||||
);
|
||||
}
|
||||
|
||||
if (upstream.primaryIpv6.length > 0) {
|
||||
parsedUpstreams.push(
|
||||
parseUpstream(upstream.primaryIpv6, "Primary", "IPv6")
|
||||
);
|
||||
}
|
||||
|
||||
if (upstream.secondaryIpv6.length > 0) {
|
||||
parsedUpstreams.push(
|
||||
parseUpstream(upstream.secondaryIpv6, "Secondary", "IPv6")
|
||||
);
|
||||
}
|
||||
|
||||
return parsedUpstreams;
|
||||
}
|
||||
);
|
||||
@@ -14,6 +14,7 @@ import { BrowserRouter, Route, Switch } from "react-router-dom";
|
||||
import "ionicons/dist/css/ionicons.min.css";
|
||||
import "font-awesome/css/font-awesome.min.css";
|
||||
import "simple-line-icons/css/simple-line-icons.css";
|
||||
import "react-bootstrap-typeahead/css/Typeahead.css";
|
||||
import "./scss/style.css";
|
||||
import Full from "./containers/Full";
|
||||
import api from "./util/api";
|
||||
|
||||
@@ -182,3 +182,7 @@
|
||||
.form-check-label {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-check-input {
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
@@ -91,5 +91,8 @@ export default {
|
||||
},
|
||||
updateDHCPInfo(settings) {
|
||||
return http.put("settings/dhcp", settings);
|
||||
},
|
||||
updateDNSInfo(settings) {
|
||||
return http.put("settings/dns", settings);
|
||||
}
|
||||
};
|
||||
|
||||
+24
-2
@@ -65,10 +65,32 @@ export function isValidIpv4(address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No segment can be longer than 3 characters or be empty
|
||||
|
||||
// All segments must be numbers (positive)
|
||||
return segments.every(
|
||||
segment => isPositiveNumber(segment) && parseInt(segment) < 256
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the string is a valid IPv4 address, and it can contain an optional
|
||||
* port after the address, separated with a #.
|
||||
* Example: 127.0.0.1#5353
|
||||
*
|
||||
* @param address {string} the address to check
|
||||
* @returns {boolean} if the address is a valid IPv4 address and the port
|
||||
* (if it exists) is valid.
|
||||
*/
|
||||
export function isValidIpv4OptionalPort(address) {
|
||||
const split = address.split("#");
|
||||
const ipv4 = split[0];
|
||||
|
||||
// Check the IPv4 address
|
||||
if (!isValidIpv4(ipv4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no port is given or if the port is valid, return true
|
||||
return (
|
||||
split.length === 1 || (split.length === 2 && isPositiveNumber(split[1]))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
isPositiveNumber,
|
||||
isValidDomain,
|
||||
isValidIpv4,
|
||||
isValidIpv4OptionalPort,
|
||||
isValidRegex
|
||||
} from "./validate";
|
||||
|
||||
@@ -188,4 +189,38 @@ describe("Testing the validation functions", () => {
|
||||
expect(isValidIpv4("555.666.777.888")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidIpv4OptionalPort", () => {
|
||||
it("passes 127.0.0.1", () => {
|
||||
expect(isValidIpv4OptionalPort("127.0.0.1")).toBe(true);
|
||||
});
|
||||
|
||||
it("passes 127.0.0.1#53", () => {
|
||||
expect(isValidIpv4OptionalPort("127.0.0.1#53")).toBe(true);
|
||||
});
|
||||
|
||||
it("passes 8.8.8.8#5353", () => {
|
||||
expect(isValidIpv4OptionalPort("8.8.8.8#5353")).toBe(true);
|
||||
});
|
||||
|
||||
it("fails 1111.1.1.1#53", () => {
|
||||
expect(isValidIpv4OptionalPort("1111.1.1.1#53")).toBe(false);
|
||||
});
|
||||
|
||||
it("fails 8.8.8.8#", () => {
|
||||
expect(isValidIpv4OptionalPort("8.8.8.8#")).toBe(false);
|
||||
});
|
||||
|
||||
it("fails 8.8.8.8##", () => {
|
||||
expect(isValidIpv4OptionalPort("8.8.8.8##")).toBe(false);
|
||||
});
|
||||
|
||||
it("fails 8.8.8.8#53#", () => {
|
||||
expect(isValidIpv4OptionalPort("8.8.8.8#53#")).toBe(false);
|
||||
});
|
||||
|
||||
it("fails 8.8.8.8#abc", () => {
|
||||
expect(isValidIpv4OptionalPort("8.8.8.8#abc")).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user