Ver código fonte

ns/sync: translations and bugfixes

Miraty 2 anos atrás
pai
commit
1d856e1e2e

+ 0 - 1
db/schema.sql

@@ -46,7 +46,6 @@ CREATE TABLE IF NOT EXISTS "ns-syncs" (
 	"username"    TEXT    NOT NULL,
 	"username"    TEXT    NOT NULL,
 	"source"      TEXT    NOT NULL,
 	"source"      TEXT    NOT NULL,
 	"destination" TEXT    NOT NULL UNIQUE,
 	"destination" TEXT    NOT NULL UNIQUE,
-	UNIQUE("username", "source", "destination"),
 	FOREIGN KEY("username") REFERENCES "users"("id"),
 	FOREIGN KEY("username") REFERENCES "users"("id"),
 	FOREIGN KEY("destination") REFERENCES "zones"("zone")
 	FOREIGN KEY("destination") REFERENCES "zones"("zone")
 );
 );

+ 1 - 3
fn/auth.php

@@ -98,11 +98,9 @@ function authDeleteUser(string $user_id): void {
 		foreach (query('select', 'registry', ['username' => $user_id], 'domain') as $domain)
 		foreach (query('select', 'registry', ['username' => $user_id], 'domain') as $domain)
 			regDeleteDomain($domain, $user_id);
 			regDeleteDomain($domain, $user_id);
 
 
-	if (in_array('ns', $user_services, true)) {
-		query('delete', 'ns-syncs', ['username' => $user_id]);
+	if (in_array('ns', $user_services, true))
 		foreach (query('select', 'zones', ['username' => $user_id], 'zone') as $zone)
 		foreach (query('select', 'zones', ['username' => $user_id], 'zone') as $zone)
 			nsDeleteZone($zone, $user_id);
 			nsDeleteZone($zone, $user_id);
-	}
 
 
 	if (in_array('ht', $user_services, true)) {
 	if (in_array('ht', $user_services, true)) {
 		foreach (query('select', 'sites', ['username' => $user_id]) as $site)
 		foreach (query('select', 'sites', ['username' => $user_id]) as $site)

+ 3 - 1
fn/ns.php

@@ -17,7 +17,7 @@ const ALLOWED_TYPES = ['AAAA', 'A', 'TXT', 'SRV', 'MX', 'SVCB', 'HTTPS', 'NS', '
 
 
 const ZONE_MAX_CHARACTERS = 10000;
 const ZONE_MAX_CHARACTERS = 10000;
 
 
-const SYNC_TTL = 3600;
+const SYNC_TTL = 10800;
 
 
 function nsParseCommonRequirements(): array {
 function nsParseCommonRequirements(): array {
 	nsCheckZonePossession($_POST['zone']);
 	nsCheckZonePossession($_POST['zone']);
@@ -73,6 +73,8 @@ function nsDeleteZone(string $zone, string $user_id): void {
 	if ($code !== 0)
 	if ($code !== 0)
 		output(500, 'Failed to purge zone data.');
 		output(500, 'Failed to purge zone data.');
 
 
+	query('delete', 'ns-syncs', ['destination' => $zone]);
+
 	// Remove from database
 	// Remove from database
 	query('delete', 'zones', [
 	query('delete', 'zones', [
 		'zone' => $zone,
 		'zone' => $zone,

+ 1 - 1
jobs/check.php

@@ -1,6 +1,6 @@
 <?php // Test that the current setup is working
 <?php // Test that the current setup is working
 
 
-require 'init.php';
+require __DIR__ . '/../init.php';
 
 
 const SFTP = '/usr/bin/sftp';
 const SFTP = '/usr/bin/sftp';
 const SSHPASS = '/usr/bin/sshpass';
 const SSHPASS = '/usr/bin/sshpass';

+ 1 - 1
jobs/delete-old-testing.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-require 'init.php';
+require __DIR__ . '/../init.php';
 
 
 const MAX_TESTING_ACCOUNT_AGE = 86400 * 10;
 const MAX_TESTING_ACCOUNT_AGE = 86400 * 10;
 
 

+ 4 - 4
jobs/ns-sync.php

@@ -1,21 +1,21 @@
 <?php
 <?php
-require 'init.php';
+require __DIR__ . '/../init.php';
 
 
 foreach (query('select', 'ns-syncs') as $sync) {
 foreach (query('select', 'ns-syncs') as $sync) {
 	$zone_raw = file_get_contents(CONF['ns']['knot_zones_path'] . '/' . $sync['destination'] . 'zone');
 	$zone_raw = file_get_contents(CONF['ns']['knot_zones_path'] . '/' . $sync['destination'] . 'zone');
 	if ($zone_raw === false)
 	if ($zone_raw === false)
 		output(403, 'Unable to read zone file.');
 		output(403, 'Unable to read zone file.');
 
 
-	foreach (['AAAA', 'A', 'SRV', 'CAA'] as $type) {
+	foreach (['AAAA', 'A', 'CAA'] as $type) {
 		// Get source/distant records
 		// Get source/distant records
 		try {
 		try {
 			$results = kdig(name: $sync['source'], type: $type);
 			$results = kdig(name: $sync['source'], type: $type);
 		} catch (KdigException) {
 		} catch (KdigException) {
-			error_log($sync['source'] . ' resolution failed.' . LF);
+			fwrite(STDERR, $sync['source'] . ' resolution failed.' . LF);
 			break;
 			break;
 		}
 		}
 		if ($results['AD'] !== 1) {
 		if ($results['AD'] !== 1) {
-			error_log($sync['source'] . ' skipped: not signed using DNSSEC.' . LF);
+			fwrite(STDERR, $sync['source'] . ' skipped: not signed using DNSSEC.' . LF);
 			continue;
 			continue;
 		}
 		}
 		$source_records = array_column($results['answerRRs'] ?? [], 'rdata' . $type);
 		$source_records = array_column($results['answerRRs'] ?? [], 'rdata' . $type);

+ 68 - 22
locales/fr/C/LC_MESSAGES/messages.po

@@ -1,7 +1,7 @@
 msgid ""
 msgid ""
 msgstr ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-06-20 00:38+0200\n"
+"POT-Creation-Date: 2023-06-26 02:14+0200\n"
 "Language: fr\n"
 "Language: fr\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 
 
@@ -226,59 +226,68 @@ msgstr "Définir la descendance d'un domaine comme alias de la descendance d'un
 msgid "Store geographic coordinates"
 msgid "Store geographic coordinates"
 msgstr "Indiquer des coordonnées géographiques"
 msgstr "Indiquer des coordonnées géographiques"
 
 
-#: pages.php:169
+#: pages.php:167
+#, php-format
+msgid "Synchronized records"
+msgstr "Enregistrements synchronisés"
+
+#: pages.php:168
+msgid "Regularly fetch distant records and update them to a local zone"
+msgstr "Récupérer régulièrement des enregistrements distants et les mettre à jour vers une zone locale"
+
+#: pages.php:174
 msgid "Web"
 msgid "Web"
 msgstr "Web"
 msgstr "Web"
 
 
-#: pages.php:170
+#: pages.php:175
 msgid "Upload a static website into an <abbr title=\"SSH File Transfer Protocol\">SFTP</abbr> space"
 msgid "Upload a static website into an <abbr title=\"SSH File Transfer Protocol\">SFTP</abbr> space"
 msgstr "Téléverser un site statique dans un espace <abbr title=\"SSH File Transfert Protocol\">SFTP</abbr>"
 msgstr "Téléverser un site statique dans un espace <abbr title=\"SSH File Transfert Protocol\">SFTP</abbr>"
 
 
-#: pages.php:173
+#: pages.php:178
 #, php-format
 #, php-format
 msgid "%s subpath access"
 msgid "%s subpath access"
 msgstr "Accès par sous-chemin de %s"
 msgstr "Accès par sous-chemin de %s"
 
 
-#: pages.php:174 pages.php:179 pages.php:184
+#: pages.php:179 pages.php:184 pages.php:189
 #, php-format
 #, php-format
 msgid "Its URL will look like %s"
 msgid "Its URL will look like %s"
 msgstr "Son URL ressemblera à %s"
 msgstr "Son URL ressemblera à %s"
 
 
-#: pages.php:174 pages.php:179 pages.php:184
+#: pages.php:179 pages.php:184 pages.php:189
 msgid "mysite"
 msgid "mysite"
 msgstr "monsite"
 msgstr "monsite"
 
 
-#: pages.php:178
+#: pages.php:183
 #, php-format
 #, php-format
 msgid "%s subdomain access"
 msgid "%s subdomain access"
 msgstr "Accès par sous-domaine de %s"
 msgstr "Accès par sous-domaine de %s"
 
 
-#: pages.php:183
+#: pages.php:188
 msgid "Dedicated domain with Let's Encrypt certificate access"
 msgid "Dedicated domain with Let's Encrypt certificate access"
 msgstr "Accès par domaine dédié avec certificat Let's Encrypt"
 msgstr "Accès par domaine dédié avec certificat Let's Encrypt"
 
 
-#: pages.php:188
+#: pages.php:193
 msgid "Onion service access"
 msgid "Onion service access"
 msgstr "Accès par service Onion"
 msgstr "Accès par service Onion"
 
 
-#: pages.php:189
+#: pages.php:194
 #, php-format
 #, php-format
 msgid "Its URL will look like %s, and work only through the Tor network"
 msgid "Its URL will look like %s, and work only through the Tor network"
 msgstr "Son URL ressemblera à %s, et ne fonctionnera que par le réseau Tor"
 msgstr "Son URL ressemblera à %s, et ne fonctionnera que par le réseau Tor"
 
 
-#: pages.php:193 pg-view/ht/del.php:18
+#: pages.php:198 pg-view/ht/del.php:18
 msgid "Delete access"
 msgid "Delete access"
 msgstr "Supprimer un accès"
 msgstr "Supprimer un accès"
 
 
-#: pages.php:194
+#: pages.php:199
 msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
 msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
 msgstr "Retirer un accès HTTP existant d'un sous-dossier de l'espace SFTP"
 msgstr "Retirer un accès HTTP existant d'un sous-dossier de l'espace SFTP"
 
 
-#: pages.php:197
+#: pages.php:202
 msgid "Manage SSH keys"
 msgid "Manage SSH keys"
 msgstr "Gérer les clés SSH"
 msgstr "Gérer les clés SSH"
 
 
-#: pages.php:198
+#: pages.php:203
 msgid "Choose what SSH key can edit what directory"
 msgid "Choose what SSH key can edit what directory"
 msgstr "Choisir quelle clé SSH peut modifier quel dossier"
 msgstr "Choisir quelle clé SSH peut modifier quel dossier"
 
 
@@ -321,11 +330,11 @@ msgstr "%sCode source%s disponible sous %s."
 msgid "Your account can't be deleted because the %s service is currently unavailable."
 msgid "Your account can't be deleted because the %s service is currently unavailable."
 msgstr "Votre compte ne peut pas être supprimé car le service %s est actuellement indisponible."
 msgstr "Votre compte ne peut pas être supprimé car le service %s est actuellement indisponible."
 
 
-#: fn/auth.php:161
+#: fn/auth.php:163
 msgid "Account rate limit reached, try again later."
 msgid "Account rate limit reached, try again later."
 msgstr "Limite de taux pour ce compte atteinte, réessayez plus tard."
 msgstr "Limite de taux pour ce compte atteinte, réessayez plus tard."
 
 
-#: fn/auth.php:186
+#: fn/auth.php:188
 msgid "Global rate limit reached, try again later."
 msgid "Global rate limit reached, try again later."
 msgstr "Limite de taux globale atteinte, réessayez plus tard."
 msgstr "Limite de taux globale atteinte, réessayez plus tard."
 
 
@@ -341,24 +350,24 @@ msgstr "<strong>Erreur de l'utilisataire</strong>&nbsp;: "
 msgid "<strong>Server error</strong>: "
 msgid "<strong>Server error</strong>: "
 msgstr "<strong>Erreur du serveur</strong>&nbsp;: "
 msgstr "<strong>Erreur du serveur</strong>&nbsp;: "
 
 
-#: fn/common.php:137
+#: fn/common.php:155
 msgid "Wrong proof."
 msgid "Wrong proof."
 msgstr "Preuve incorrecte."
 msgstr "Preuve incorrecte."
 
 
-#: fn/dns.php:74
+#: fn/dns.php:77
 msgid "IP address malformed."
 msgid "IP address malformed."
 msgstr "Adresse IP malformée."
 msgstr "Adresse IP malformée."
 
 
-#: fn/dns.php:79 fn/ht.php:48
+#: fn/dns.php:82 fn/ht.php:48
 msgid "Domain malformed."
 msgid "Domain malformed."
 msgstr "Domaine malformé."
 msgstr "Domaine malformé."
 
 
-#: fn/ns.php:31 pg-act/ns/edit.php:25
+#: fn/ns.php:33 pg-act/ns/edit.php:25
 #, php-format
 #, php-format
 msgid "TTLs shorter than %s seconds are forbidden."
 msgid "TTLs shorter than %s seconds are forbidden."
 msgstr "Les TTLs plus courts que %s secondes sont interdits."
 msgstr "Les TTLs plus courts que %s secondes sont interdits."
 
 
-#: fn/ns.php:33 pg-act/ns/edit.php:27
+#: fn/ns.php:35 pg-act/ns/edit.php:27
 #, php-format
 #, php-format
 msgid "TTLs longer than %s seconds are forbidden."
 msgid "TTLs longer than %s seconds are forbidden."
 msgstr "Les TTLs plus longs que %s secondes sont interdits."
 msgstr "Les TTLs plus longs que %s secondes sont interdits."
@@ -499,6 +508,14 @@ msgstr "Le type %s n'est pas autorisé."
 msgid "Sent zone content is not correct (according to <code>kzonecheck</code>)."
 msgid "Sent zone content is not correct (according to <code>kzonecheck</code>)."
 msgstr "Le contenu de zone envoyé n'est pas correct (selon <code>kzonecheck</code>)."
 msgstr "Le contenu de zone envoyé n'est pas correct (selon <code>kzonecheck</code>)."
 
 
+#: pg-act/ns/sync.php:19
+msgid "Multiple source domains can't be applied to the same target domain."
+msgstr "Plusieurs domaines sources ne peuvent pas être appliqués sur un même domaine cible."
+
+#: pg-act/ns/sync.php:41
+msgid "Synchronized records updated."
+msgstr "Enregistrements synchronisés mis à jour."
+
 #: pg-act/ns/zone-add.php:6
 #: pg-act/ns/zone-add.php:6
 msgid "This zone already exists on the service."
 msgid "This zone already exists on the service."
 msgstr "Cette zone existe déjà sur ce service."
 msgstr "Cette zone existe déjà sur ce service."
@@ -858,7 +875,7 @@ msgstr "Clé publique"
 msgid "Allowed directory"
 msgid "Allowed directory"
 msgstr "Dossier autorisé"
 msgstr "Dossier autorisé"
 
 
-#: pg-view/ht/keys.php:30
+#: pg-view/ht/keys.php:30 pg-view/ns/sync.php:37
 msgid "Update"
 msgid "Update"
 msgstr "Mettre à jour"
 msgstr "Mettre à jour"
 
 
@@ -1109,6 +1126,35 @@ msgstr "Type de hash"
 msgid "Fingerprint"
 msgid "Fingerprint"
 msgstr "Empreinte"
 msgstr "Empreinte"
 
 
+#: pg-view/ns/sync.php:2
+#, php-format
+msgid "AAAA, A and CAA records are regularly copied from the source domain to the target domain. Their TTLs are set to %s seconds."
+msgstr "Les enregistrements AAAA, A et CAA sont régulièrement copiés du domain source vers le domain cible. Leurs TTLs sont définis à %s secondes."
+
+#: pg-view/ns/sync.php:5
+msgid "Source domains that are not signed with DNSSEC are not synchronized. Synchronizations that remain broken may be deleted."
+msgstr "Les domains sources qui ne sont pas signés avec DNSSEC ne sont pas synchronisés. Les synchronisations qui restent cassées peuvent être supprimées."
+
+#: pg-view/ns/sync.php:8
+msgid "This is meant to be used for apex domains, where CNAME records are not allowed. For non-apex domains, CNAME records should be used instead."
+msgstr "Ceci est destiné à être utilisé sur des domaines apex, où les enregistrements CNAME ne sont pas autorisés. Pour des domaines non-apex, les enregistrements CNAME devraient être utilisés à la place."
+
+#: pg-view/ns/sync.php:16
+msgid "Add new domain records to be synchronized"
+msgstr "Ajouter de nouveaux enregistrements de domain à synchroniser"
+
+#: pg-view/ns/sync.php:16
+msgid "Synchronized domain"
+msgstr "Domaine synchronisé"
+
+#: pg-view/ns/sync.php:18
+msgid "Source domain"
+msgstr "Domaine source"
+
+#: pg-view/ns/sync.php:22
+msgid "Target domain"
+msgstr "Domaine cible"
+
 #: pg-view/ns/tlsa.php:4
 #: pg-view/ns/tlsa.php:4
 msgid "Use"
 msgid "Use"
 msgstr "Utilisation"
 msgstr "Utilisation"

+ 68 - 22
locales/messages.pot

@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-06-20 00:38+0200\n"
+"POT-Creation-Date: 2023-06-26 02:14+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -238,59 +238,68 @@ msgstr ""
 msgid "Store geographic coordinates"
 msgid "Store geographic coordinates"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:169
+#: pages.php:167
+#, php-format
+msgid "Synchronized records"
+msgstr ""
+
+#: pages.php:168
+msgid "Regularly fetch distant records and update them to a local zone"
+msgstr ""
+
+#: pages.php:174
 msgid "Web"
 msgid "Web"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:170
+#: pages.php:175
 msgid "Upload a static website into an <abbr title=\"SSH File Transfer Protocol\">SFTP</abbr> space"
 msgid "Upload a static website into an <abbr title=\"SSH File Transfer Protocol\">SFTP</abbr> space"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:173
+#: pages.php:178
 #, php-format
 #, php-format
 msgid "%s subpath access"
 msgid "%s subpath access"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:174 pages.php:179 pages.php:184
+#: pages.php:179 pages.php:184 pages.php:189
 #, php-format
 #, php-format
 msgid "Its URL will look like %s"
 msgid "Its URL will look like %s"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:174 pages.php:179 pages.php:184
+#: pages.php:179 pages.php:184 pages.php:189
 msgid "mysite"
 msgid "mysite"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:178
+#: pages.php:183
 #, php-format
 #, php-format
 msgid "%s subdomain access"
 msgid "%s subdomain access"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:183
+#: pages.php:188
 msgid "Dedicated domain with Let's Encrypt certificate access"
 msgid "Dedicated domain with Let's Encrypt certificate access"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:188
+#: pages.php:193
 msgid "Onion service access"
 msgid "Onion service access"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:189
+#: pages.php:194
 #, php-format
 #, php-format
 msgid "Its URL will look like %s, and work only through the Tor network"
 msgid "Its URL will look like %s, and work only through the Tor network"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:193 pg-view/ht/del.php:18
+#: pages.php:198 pg-view/ht/del.php:18
 msgid "Delete access"
 msgid "Delete access"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:194
+#: pages.php:199
 msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
 msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:197
+#: pages.php:202
 msgid "Manage SSH keys"
 msgid "Manage SSH keys"
 msgstr ""
 msgstr ""
 
 
-#: pages.php:198
+#: pages.php:203
 msgid "Choose what SSH key can edit what directory"
 msgid "Choose what SSH key can edit what directory"
 msgstr ""
 msgstr ""
 
 
@@ -333,11 +342,11 @@ msgstr ""
 msgid "Your account can't be deleted because the %s service is currently unavailable."
 msgid "Your account can't be deleted because the %s service is currently unavailable."
 msgstr ""
 msgstr ""
 
 
-#: fn/auth.php:161
+#: fn/auth.php:163
 msgid "Account rate limit reached, try again later."
 msgid "Account rate limit reached, try again later."
 msgstr ""
 msgstr ""
 
 
-#: fn/auth.php:186
+#: fn/auth.php:188
 msgid "Global rate limit reached, try again later."
 msgid "Global rate limit reached, try again later."
 msgstr ""
 msgstr ""
 
 
@@ -353,24 +362,24 @@ msgstr ""
 msgid "<strong>Server error</strong>: "
 msgid "<strong>Server error</strong>: "
 msgstr ""
 msgstr ""
 
 
-#: fn/common.php:137
+#: fn/common.php:155
 msgid "Wrong proof."
 msgid "Wrong proof."
 msgstr ""
 msgstr ""
 
 
-#: fn/dns.php:74
+#: fn/dns.php:77
 msgid "IP address malformed."
 msgid "IP address malformed."
 msgstr ""
 msgstr ""
 
 
-#: fn/dns.php:79 fn/ht.php:48
+#: fn/dns.php:82 fn/ht.php:48
 msgid "Domain malformed."
 msgid "Domain malformed."
 msgstr ""
 msgstr ""
 
 
-#: fn/ns.php:31 pg-act/ns/edit.php:25
+#: fn/ns.php:33 pg-act/ns/edit.php:25
 #, php-format
 #, php-format
 msgid "TTLs shorter than %s seconds are forbidden."
 msgid "TTLs shorter than %s seconds are forbidden."
 msgstr ""
 msgstr ""
 
 
-#: fn/ns.php:33 pg-act/ns/edit.php:27
+#: fn/ns.php:35 pg-act/ns/edit.php:27
 #, php-format
 #, php-format
 msgid "TTLs longer than %s seconds are forbidden."
 msgid "TTLs longer than %s seconds are forbidden."
 msgstr ""
 msgstr ""
@@ -511,6 +520,14 @@ msgstr ""
 msgid "Sent zone content is not correct (according to <code>kzonecheck</code>)."
 msgid "Sent zone content is not correct (according to <code>kzonecheck</code>)."
 msgstr ""
 msgstr ""
 
 
+#: pg-act/ns/sync.php:19
+msgid "Multiple source domains can't be applied to the same target domain."
+msgstr ""
+
+#: pg-act/ns/sync.php:41
+msgid "Synchronized records updated."
+msgstr ""
+
 #: pg-act/ns/zone-add.php:6
 #: pg-act/ns/zone-add.php:6
 msgid "This zone already exists on the service."
 msgid "This zone already exists on the service."
 msgstr ""
 msgstr ""
@@ -870,7 +887,7 @@ msgstr ""
 msgid "Allowed directory"
 msgid "Allowed directory"
 msgstr ""
 msgstr ""
 
 
-#: pg-view/ht/keys.php:30
+#: pg-view/ht/keys.php:30 pg-view/ns/sync.php:37
 msgid "Update"
 msgid "Update"
 msgstr ""
 msgstr ""
 
 
@@ -1121,6 +1138,35 @@ msgstr ""
 msgid "Fingerprint"
 msgid "Fingerprint"
 msgstr ""
 msgstr ""
 
 
+#: pg-view/ns/sync.php:2
+#, php-format
+msgid "AAAA, A and CAA records are regularly copied from the source domain to the target domain. Their TTLs are set to %s seconds."
+msgstr ""
+
+#: pg-view/ns/sync.php:5
+msgid "Source domains that are not signed with DNSSEC are not synchronized. Synchronizations that remain broken may be deleted."
+msgstr ""
+
+#: pg-view/ns/sync.php:8
+msgid "This is meant to be used for apex domains, where CNAME records are not allowed. For non-apex domains, CNAME records should be used instead."
+msgstr ""
+
+#: pg-view/ns/sync.php:16
+msgid "Add new domain records to be synchronized"
+msgstr ""
+
+#: pg-view/ns/sync.php:16
+msgid "Synchronized domain"
+msgstr ""
+
+#: pg-view/ns/sync.php:18
+msgid "Source domain"
+msgstr ""
+
+#: pg-view/ns/sync.php:22
+msgid "Target domain"
+msgstr ""
+
 #: pg-view/ns/tlsa.php:4
 #: pg-view/ns/tlsa.php:4
 msgid "Use"
 msgid "Use"
 msgstr ""
 msgstr ""

+ 1 - 1
pages.php

@@ -165,7 +165,7 @@ define('PAGES', [
 		],
 		],
 		'sync' => [
 		'sync' => [
 			'title' => sprintf(_('Synchronized records')),
 			'title' => sprintf(_('Synchronized records')),
-			'description' => _('Regularly fetch distant record value and update it to a local zone'),
+			'description' => _('Regularly fetch distant records and update them to a local zone'),
 			'tokens_account_cost' => 900,
 			'tokens_account_cost' => 900,
 		],
 		],
 	],
 	],

+ 5 - 1
pg-act/ns/sync.php

@@ -4,7 +4,7 @@ $el_nb = count($_POST['syncs']);
 if ($el_nb < 1 OR $el_nb > 8)
 if ($el_nb < 1 OR $el_nb > 8)
 	output(403, 'Wrong elements number.');
 	output(403, 'Wrong elements number.');
 
 
-foreach ($_POST['syncs'] as $i => &$sync) {
+foreach ($_POST['syncs'] as $i => $sync) {
 	if (($sync['source'] ?? '') === '') {
 	if (($sync['source'] ?? '') === '') {
 		unset($_POST['syncs'][$i]);
 		unset($_POST['syncs'][$i]);
 		continue;
 		continue;
@@ -14,6 +14,10 @@ foreach ($_POST['syncs'] as $i => &$sync) {
 }
 }
 $syncs = array_values($_POST['syncs']);
 $syncs = array_values($_POST['syncs']);
 
 
+$destinations = array_column($syncs, 'destination');
+if (count($destinations) !== count(array_unique($destinations)))
+	output(403, _('Multiple source domains can\'t be applied to the same target domain.'));
+
 rateLimit();
 rateLimit();
 
 
 try {
 try {

+ 3 - 3
pg-view/ns/sync.php

@@ -5,7 +5,7 @@
 	<?= _('Source domains that are not signed with DNSSEC are not synchronized. Synchronizations that remain broken may be deleted.') ?>
 	<?= _('Source domains that are not signed with DNSSEC are not synchronized. Synchronizations that remain broken may be deleted.') ?>
 </p>
 </p>
 <p>
 <p>
-	<?= _('This is meant to be used for apex domains, where a CNAME record is not allowed. For non-apex domains, CNAME records should be used instead.') ?>
+	<?= _('This is meant to be used for apex domains, where CNAME records are not allowed. For non-apex domains, CNAME records should be used instead.') ?>
 </p>
 </p>
 
 
 <form method="post">
 <form method="post">
@@ -13,7 +13,7 @@
 foreach (array_slice(array_merge(query('select', 'ns-syncs', ['username' => $_SESSION['id'] ?? '']), [['source' => '', 'destination' => '—']]), 0, 8) as $i => $sync) {
 foreach (array_slice(array_merge(query('select', 'ns-syncs', ['username' => $_SESSION['id'] ?? '']), [['source' => '', 'destination' => '—']]), 0, 8) as $i => $sync) {
 ?>
 ?>
 	<fieldset>
 	<fieldset>
-		<legend><?= ($sync['source'] === '') ? _('Add new domain records to be synchronized') : _('Synchronized record') ?></legend>
+		<legend><?= ($sync['source'] === '') ? _('Add new domain records to be synchronized') : _('Synchronized domain') ?></legend>
 		<div>
 		<div>
 			<label for="source<?= $i ?>"><?= _('Source domain') ?></label><br>
 			<label for="source<?= $i ?>"><?= _('Source domain') ?></label><br>
 			<input placeholder="provider.<?= PLACEHOLDER_DOMAIN ?>." id="source<?= $i ?>" name="syncs[<?= $i ?>][source]" value="<?= $sync['source'] ?>" type="text">
 			<input placeholder="provider.<?= PLACEHOLDER_DOMAIN ?>." id="source<?= $i ?>" name="syncs[<?= $i ?>][source]" value="<?= $sync['source'] ?>" type="text">
@@ -24,7 +24,7 @@ foreach (array_slice(array_merge(query('select', 'ns-syncs', ['username' => $_SE
 			<select required="" name="syncs[<?= $i ?>][destination]" id="destination<?= $i ?>">
 			<select required="" name="syncs[<?= $i ?>][destination]" id="destination<?= $i ?>">
 				<option <?= (($sync['destination'] === '') ? 'value="" disabled=""' : 'value="' . $sync['destination'] . '"') ?> selected=""><?= $sync['destination'] ?></option>
 				<option <?= (($sync['destination'] === '') ? 'value="" disabled=""' : 'value="' . $sync['destination'] . '"') ?> selected=""><?= $sync['destination'] ?></option>
 <?php
 <?php
-foreach (nsListUserZones() as $zone)
+foreach (array_diff(nsListUserZones(), query('select', 'ns-syncs', ['username' => $_SESSION['id'] ?? ''], 'destination')) as $zone)
 	echo "<option value='" . $zone . "'>" . $zone . "</option>";
 	echo "<option value='" . $zone . "'>" . $zone . "</option>";
 ?>
 ?>