-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcard.html
More file actions
153 lines (149 loc) · 6.92 KB
/
Copy pathcard.html
File metadata and controls
153 lines (149 loc) · 6.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
{# Authenticated phone-pairing card with QR for the selected gateway URL.
The SVG is inlined so the browser does not need a second request (img tags
cannot attach the WebUI bearer header). `token_options` lists the bootstrap
token plus every device token, marking ones this process cannot currently
display for QR purposes - only cached plaintexts can be encoded. That
device's own pairing is completely unaffected either way: the phone keeps
authenticating with its already-scanned secret regardless of whether this
process can still show it. `token_status` explains why the shown token
differs from what was asked for: `stale` means the requested device token
still works fine but this process cannot redisplay its secret (typically
after a restart) - rotating is optional and only needed to get a fresh,
viewable QR, for example to re-pair a lost phone. `unknown` means it no
longer exists at all (typically already revoked).
Network exposure callout is *not* rendered here: HTMX replaces
#pairing-card on every token/url change, so a sibling panel outside that
id would accumulate duplicates. Pair & test mounts it once at page end. #}
<div class="card" id="pairing-card">
<div class="section-heading">
<h2>Pair phone</h2>
</div>
<p class="muted pairing-lead">Scan in VocaPhone on iPhone or Android.
The QR includes the live token, so keep this page private while it is on screen.</p>
{% if token_status == "stale" %}
<div class="callout pairing-token-notice" role="status">
<p class="pairing-token-notice-body">
<strong>{{ requested_token_label or "That device token" }} is still paired and working normally.</strong>
After a server restart this session cannot show its secret again
(we never store it for recovery). Showing the bootstrap token instead.
Only rotate if you need a new QR, for example to re-pair a lost phone.
</p>
<div class="pairing-token-actions">
<form hx-post="/ui/partials/pairing/tokens/{{ requested_token_id | urlpath }}/rotate"
hx-target="#pairing-card" hx-swap="outerHTML">
<input type="hidden" name="url" value="{{ selected_url }}" />
<button type="submit" class="primary small">Rotate & show a new QR</button>
</form>
<p class="muted small pairing-token-rotate-hint">Rotate only if you need it:
the current secret stops working immediately and the device must pair again.</p>
</div>
</div>
{% elif token_status == "unknown" %}
<div class="callout warning pairing-token-notice" role="status">
<p class="pairing-token-notice-body">That device token no longer exists
(it may have been revoked). Showing the bootstrap token instead.
Create a new device token below for a fresh QR.</p>
</div>
{% endif %}
<div class="pairing-layout">
<div class="pairing-qr-wrap">
<button type="button" class="pairing-qr pairing-qr-copy" id="copy-pairing-qr"
data-url="{{ selected_url or "" }}"
data-token="{{ token_plaintext or "" }}"
title="Click to copy gateway address and token"
aria-label="Pairing QR for {{ selected_url }}. Click to copy
gateway address and token.">
{{ qr_svg_inline }}
<span class="pairing-qr-hint" aria-hidden="true">Click to copy</span>
</button>
</div>
<div class="pairing-meta">
<div class="pairing-fields">
<label>
<span>Gateway address</span>
<select name="url"
hx-get="/ui/partials/pairing"
hx-target="#pairing-card"
hx-swap="outerHTML"
hx-trigger="change"
hx-include="this,[name='token_id']">
{% for url in candidates %}
<option value="{{ url }}"{% if url == selected_url %} selected{% endif %}>{{ url }}</option>
{% endfor %}
</select>
</label>
<label>
<span>Token</span>
<select name="token_id"
hx-get="/ui/partials/pairing"
hx-target="#pairing-card"
hx-swap="outerHTML"
hx-trigger="change"
hx-include="this,[name='url']">
{% for token_id, label in token_options %}
<option value="{{ token_id }}"{% if token_id == selected_token_id %} selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</label>
</div>
<p class="pairing-encode-summary muted small">
Encodes <code class="pairing-code">{{ selected_url }}</code>
with <strong>{{ selected_token_label }}</strong>
<span class="pairing-token-hint">({{ token_redacted }})</span>
</p>
</div>
</div>
<details class="pairing-advanced">
<summary>Custom address & device access</summary>
<div class="pairing-advanced-grid">
<form class="pairing-panel" hx-get="/ui/partials/pairing"
hx-target="#pairing-card" hx-swap="outerHTML">
<input type="hidden" name="token_id" value="{{ selected_token_id }}" />
<label>
<span>Custom address</span>
<span class="field-hint">Tailscale IP, MagicDNS, or other host the phone
can reach</span>
<div class="row">
<input name="url" type="text"
placeholder="100.x.x.x or phone.tailnet.ts.net"
autocomplete="off" spellcheck="false" />
<button type="submit" class="ghost small">Use address</button>
</div>
</label>
</form>
<form class="pairing-panel" hx-post="/ui/partials/pairing/tokens"
hx-target="#pairing-card" hx-swap="outerHTML">
<input type="hidden" name="url" value="{{ selected_url }}" />
<label>
<span>New device token</span>
<span class="field-hint">Or pair a new device with its own token;
revoke later in Settings</span>
<div class="row">
<input name="label" type="text" required maxlength="100"
placeholder="e.g. Kanishk's iPhone" autocomplete="off" />
<button type="submit" class="ghost small">Create & show QR</button>
</div>
</label>
</form>
</div>
{% if saved_urls %}
<div class="pairing-saved">
<h4 class="pairing-subhead">Saved addresses</h4>
<ul>
{% for url in saved_urls %}
<li><code>{{ url }}</code>
<button type="button" class="ghost small danger"
hx-delete="/ui/partials/pairing?url={{ url | urlpath }}"
hx-target="#pairing-card" hx-swap="outerHTML"
hx-confirm="Remove {{ url }} from saved addresses?">Remove</button></li>
{% endfor %}
</ul>
</div>
{% endif %}
</details>
<p class="pairing-note">
Prefer LAN Wi-Fi when the phone is on the same network; use Tailscale when
both are on the tailnet.
<code>VOCAGATEWAY_PUBLIC_URL</code> overrides discovery.
</p>
</div>