Selaa lähdekoodia

Ask for the reprovision passphrase when you ask to reprovision

The secrets passphrase sat in the guest listing as a password box beside
a Reprovision button, so every guest an admin could rebuild carried an
open field asking to be filled in, and the only thing between a stray
click and a destroyed guest was a confirm() nobody reads.

Put both in a popover the Reprovision button opens: the warning about
what a reprovision does to anything the recipe does not describe, the
passphrase, a Cancel, and the button which actually launches it.  The
popover lives inside the form -- showing it does not move it in the DOM
-- so the passphrase still posts with the guest and the route it came
from, and nothing behind /guest/reprovision changes.

The button carries both command/commandfor and popovertarget: a browser
with popover but no invoker commands would otherwise have no way to open
this, and where both are understood the command wins and the target is
ignored.  Older than both, the block draws inline and submits, which is
the form this replaces minus the confirmation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Aevq96J12mMxJnexthkMHv
George Baugh 1 kuukausi sitten
vanhempi
sitoutus
7b752d3354
2 muutettua tiedostoa jossa 58 lisäystä ja 7 poistoa
  1. 24 0
      www/styles/screen.css
  2. 34 7
      www/templates/html/components/forms/provisioned_guests.tx

+ 24 - 0
www/styles/screen.css

@@ -512,3 +512,27 @@ pre {
 .bg-red {
     background-color: red;
 }
+
+/* The reprovision confirmation -- see forms/provisioned_guests.tx.  The
+   passphrase lives in here rather than in the page, so it is asked for at the
+   point of confirming rather than sitting in a box nobody meant to fill in.
+   A browser with no popover support ignores the attribute and draws this
+   inline, where it still works as a form. */
+.guest-reprovision-popover {
+    max-width: 32rem;
+    padding: 1rem;
+    background: #e4e4e4;
+    border: 1px solid grey;
+    border-radius: .5rem;
+}
+.guest-reprovision-popover::backdrop {
+    background: rgba( 0, 0, 0, .5 );
+}
+.guest-reprovision-warning {
+    margin-top: 0;
+}
+.guest-reprovision-actions {
+    margin-top: .75rem;
+    display: flex;
+    gap: .5rem;
+}

+ 34 - 7
www/templates/html/components/forms/provisioned_guests.tx

@@ -100,13 +100,40 @@
                 <form class="Submissions guest-reprovision" action="/guest/reprovision" method="POST" autocomplete="off">
                     <input type="hidden" name="guest" value="<: $post.domain :>" />
                     <input type="hidden" name="to" value="<: $route :>" />
-                    <label>
-                        Secrets passphrase
-                        <input required class="cooltext" type="password" name="passphrase" autocomplete="off"
-                               title="new_config asks for this on stdin; it is used for this run and not stored" />
-                    </label>
-                    <button class="coolbutton guest-destroy" type="submit"
-                            onclick="return confirm('Reprovision <: $post.title :>?  This destroys the guest and builds it again from its recipe.  Anything on it the recipe does not describe will not come back.');">Reprovision</button>
+                    : # The passphrase is asked for in the popover rather than
+                    : # sat in the page, so nothing is typed until Reprovision is
+                    : # pressed, and the confirmation is a button you have to hit
+                    : # rather than a confirm() nobody reads.  The popover stays
+                    : # inside the form, so the passphrase is submitted with it.
+                    : # popovertarget as well as the command attributes, since a
+                    : # browser which has popover but not invoker commands would
+                    : # otherwise have no way to open this at all.  Where both are
+                    : # understood the command wins and the target is ignored, so
+                    : # they do not fight.
+                    <button class="coolbutton guest-destroy" type="button"
+                            command="show-popover" commandfor="reprovision-<: $post.id :>"
+                            popovertarget="reprovision-<: $post.id :>" popovertargetaction="show">Reprovision</button>
+                    : # A browser old enough to have neither draws this inline and
+                    : # submits it, which is the form this replaced, minus the
+                    : # confirmation.
+                    <div popover id="reprovision-<: $post.id :>" class="guest-reprovision-popover">
+                        <p class="guest-reprovision-warning">
+                            Reprovision <: $post.title :>?
+                            This destroys the guest and builds it again from its recipe.
+                            Anything on it the recipe does not describe will not come back.
+                        </p>
+                        <label>
+                            Secrets passphrase
+                            <input required autofocus class="cooltext" type="password" name="passphrase" autocomplete="off"
+                                   title="new_config asks for this on stdin; it is used for this run and not stored" />
+                        </label>
+                        <div class="guest-reprovision-actions">
+                            <button class="coolbutton" type="button"
+                                    command="hide-popover" commandfor="reprovision-<: $post.id :>"
+                                    popovertarget="reprovision-<: $post.id :>" popovertargetaction="hide">Cancel</button>
+                            <button class="coolbutton guest-destroy" type="submit">Reprovision it</button>
+                        </div>
+                    </div>
                 </form>
                 : }
                 : }