diff --git a/php/public/index.php b/php/public/index.php index 2eea1ddb..531962cf 100644 --- a/php/public/index.php +++ b/php/public/index.php @@ -183,6 +183,7 @@ $app->get('/containers', function (Request $request, Response $response, array $ 'bypass_container_update' => $bypass_container_update, 'desec_email' => $configurationManager->desecEmail, 'is_desec_domain' => $configurationManager->isDesecDomain(), + 'desec_account_registered' => $configurationManager->isDesecAccountRegistered(), ]); })->setName('profile'); $app->get('/login', function (Request $request, Response $response, array $args) use ($container) { diff --git a/php/src/Controller/DesecController.php b/php/src/Controller/DesecController.php index a662c9a4..ca7918c9 100644 --- a/php/src/Controller/DesecController.php +++ b/php/src/Controller/DesecController.php @@ -36,10 +36,19 @@ readonly class DesecController { return $response->withStatus(422); } - $email = trim((string)($request->getParsedBody()['desec_email'] ?? '')); - if ($email === '' || filter_var($email, FILTER_VALIDATE_EMAIL) === false) { - $response->getBody()->write('Please provide a valid email address.'); - return $response->withStatus(422); + // When a deSEC account was already registered (token exists) but domain creation previously + // failed, we skip account registration and re-use the stored token and email. + $accountAlreadyRegistered = $this->configurationManager->isDesecAccountRegistered(); + + if ($accountAlreadyRegistered) { + $token = $this->configurationManager->getDesecToken(); + // email is already stored; no need to validate or update it + } else { + $email = trim((string)($request->getParsedBody()['desec_email'] ?? '')); + if ($email === '' || filter_var($email, FILTER_VALIDATE_EMAIL) === false) { + $response->getBody()->write('Please provide a valid email address.'); + return $response->withStatus(422); + } } $slug = trim((string)($request->getParsedBody()['desec_slug'] ?? '')); @@ -52,17 +61,26 @@ readonly class DesecController { } try { - // Register an account at deSEC and obtain an API token - $password = bin2hex(random_bytes(24)); - $token = $this->registerDesecAccount($email, $password); + if (!$accountAlreadyRegistered) { + // Register an account at deSEC and obtain an API token. + // The password is intentionally ephemeral: only the API token is needed for + // subsequent calls, so the password does not need to be stored. + $password = bin2hex(random_bytes(24)); + $token = $this->registerDesecAccount($email, $password); + + // Persist the token and email immediately so that a subsequent domain-registration + // failure leaves the account credentials stored and allows the user to retry. + $this->configurationManager->startTransaction(); + $this->configurationManager->setDesecToken($token); + $this->configurationManager->desecEmail = $email; + $this->configurationManager->commitTransaction(); + } // Register a free dedyn.io subdomain $domain = $this->registerDesecDomain($token, $slug); - // Persist the credentials and auto-enable caddy as the reverse proxy + // Auto-enable caddy and dnsmasq (idempotent — safe to call even on retry) $this->configurationManager->startTransaction(); - $this->configurationManager->setDesecToken($token); - $this->configurationManager->desecEmail = $email; $enabled = array_values(array_filter( $this->configurationManager->aioCommunityContainers, fn(string $cc): bool => $cc !== '', diff --git a/php/src/Data/ConfigurationManager.php b/php/src/Data/ConfigurationManager.php index 72391260..b4ad58b0 100644 --- a/php/src/Data/ConfigurationManager.php +++ b/php/src/Data/ConfigurationManager.php @@ -228,6 +228,15 @@ class ConfigurationManager return str_ends_with($this->domain, '.dedyn.io') && $this->getDesecToken() !== ''; } + /** + * Returns true when a deSEC account token is stored but no domain has been configured yet. + * This happens when account registration succeeded but domain registration subsequently failed. + * In this state the user can retry domain registration with a different slug. + */ + public function isDesecAccountRegistered(): bool { + return $this->getDesecToken() !== '' && $this->desecEmail !== '' && $this->domain === ''; + } + public string $apachePort { get => $this->getEnvironmentalVariableOrConfig('APACHE_PORT', 'apache_port', '443'); set { $this->set('apache_port', $value); } diff --git a/php/templates/containers.twig b/php/templates/containers.twig index fc4d9d72..aeda31b3 100644 --- a/php/templates/containers.twig +++ b/php/templates/containers.twig @@ -132,18 +132,28 @@ {% endif %}
Hint: If the domain validation fails but you are completely sure that you've configured everything correctly, you may skip the domain validation by following this documentation.
-deSEC offers free dynamic DNS subdomains under dedyn.io. AIO can register an account and a subdomain for you automatically. The caddy community container will be enabled as a reverse proxy, the dnsmasq container will be enabled for local DNS resolution, and the mastercontainer will keep your DNS record up to date automatically.
-Please enter your email address. You can also enter a desired subdomain slug (the part before .dedyn.io); leave it blank for a random one.
Note: By submitting this form you agree to the deSEC terms of service. The registered domain and your deSEC account credentials are stored in the AIO configuration. After registration, set your router's DHCP DNS server to this machine's local IP address so LAN devices resolve the domain locally (see the dnsmasq documentation). Alternatively adjust the hosts files on your clients so that they can reach the server using the local ip-address.
+ {% if desec_account_registered %} +Your deSEC account ({{ desec_email }}) was registered successfully but the domain could not be registered. Please enter a desired subdomain slug (the part before .dedyn.io) and try again, or leave it blank for a random one.
Please enter your email address. You can also enter a desired subdomain slug (the part before .dedyn.io); leave it blank for a random one.
Note: By submitting this form you agree to the deSEC terms of service. The registered domain and your deSEC account credentials are stored in the AIO configuration. After registration, set your router's DHCP DNS server to this machine's local IP address so LAN devices resolve the domain locally (see the dnsmasq documentation). Alternatively adjust the hosts files on your clients so that they can reach the server using the local ip-address.
+ {% endif %}