Pasting the address you use for Officer produced http://https://platform.officer.dev:18792/ — the scheme was taken as part of the hostname and http:// prepended anyway. It failed as "Relay not reachable", which reads as the relay being down rather than the URL being mangled. normalizeHost strips a scheme, a path and credentials, keeps IPv6 brackets, and lifts a port out of the address so pasting host:9000 moves the port field instead of dropping it. The field is rewritten with the result, so what is shown is what is stored. The scheme is now always https, and wss for the socket, whatever was typed. The relay carries a pairing token on every request and drives a browser someone is signed into; plaintext because a field was left blank is not a default worth having. CONSEQUENCE, stated rather than discovered: the relay itself still serves plain HTTP on 18792, so the extension cannot connect until it is behind TLS. That is a proxy host away and it is the right shape — the raw port was never reachable from anywhere but the tailnet anyway. The unreachable message now says what was tried and that the port is normally private, instead of "verify the server address", which is true and useless when the address is fine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
83 lines
3.0 KiB
JavaScript
83 lines
3.0 KiB
JavaScript
import { classifyRelayCheckException, classifyRelayCheckResponse, normalizeHost, relayUrl } from './options-validation.js'
|
|
|
|
const DEFAULT_PORT = 18792
|
|
const DEFAULT_HOST = '127.0.0.1'
|
|
|
|
function clampPort(value) {
|
|
const n = Number.parseInt(String(value || ''), 10)
|
|
if (!Number.isFinite(n)) return DEFAULT_PORT
|
|
if (n <= 0 || n > 65535) return DEFAULT_PORT
|
|
return n
|
|
}
|
|
|
|
function updateRelayUrl(host, port) {
|
|
const el = document.getElementById('relay-url')
|
|
if (!el) return
|
|
el.textContent = relayUrl(host, port, '/')
|
|
}
|
|
|
|
function setStatus(kind, message) {
|
|
const status = document.getElementById('status')
|
|
if (!status) return
|
|
status.dataset.kind = kind || ''
|
|
status.textContent = message || ''
|
|
}
|
|
|
|
async function checkRelayReachable(host, port, token) {
|
|
const url = relayUrl(host, port, '/json/version')
|
|
const trimmedToken = String(token || '').trim()
|
|
if (!trimmedToken) {
|
|
setStatus('error', 'Relay token required. Save your token to connect.')
|
|
return
|
|
}
|
|
try {
|
|
// Token is already derived server-side — use it directly
|
|
const res = await chrome.runtime.sendMessage({
|
|
type: 'relayCheck',
|
|
url,
|
|
token: trimmedToken,
|
|
})
|
|
const result = classifyRelayCheckResponse(res, host, port)
|
|
if (result.action === 'throw') throw new Error(result.error)
|
|
setStatus(result.kind, result.message)
|
|
} catch (err) {
|
|
const result = classifyRelayCheckException(err, host, port)
|
|
setStatus(result.kind, result.message)
|
|
}
|
|
}
|
|
|
|
async function load() {
|
|
const stored = await chrome.storage.local.get(['relayPort', 'relayHost', 'relayToken'])
|
|
const port = clampPort(stored.relayPort)
|
|
const host = normalizeHost(stored.relayHost).host || DEFAULT_HOST
|
|
const token = String(stored.relayToken || '').trim()
|
|
document.getElementById('host').value = host
|
|
document.getElementById('port').value = String(port)
|
|
document.getElementById('token').value = token
|
|
updateRelayUrl(host, port)
|
|
await checkRelayReachable(host, port, token)
|
|
}
|
|
|
|
async function save() {
|
|
const hostInput = document.getElementById('host')
|
|
const portInput = document.getElementById('port')
|
|
const tokenInput = document.getElementById('token')
|
|
// Normalised on the way in, and the field is rewritten with the result — so what is stored and what is
|
|
// shown are the same string, and a pasted `https://host/path` visibly becomes `host` rather than being
|
|
// quietly reinterpreted.
|
|
const parsed = normalizeHost(hostInput.value)
|
|
const host = parsed.host || DEFAULT_HOST
|
|
// A port in the address wins over a stale one in the port box: someone pasting `host:9000` means 9000.
|
|
const port = clampPort(parsed.port ?? portInput.value)
|
|
const token = String(tokenInput.value || '').trim()
|
|
await chrome.storage.local.set({ relayHost: host, relayPort: port, relayToken: token })
|
|
hostInput.value = host
|
|
portInput.value = String(port)
|
|
tokenInput.value = token
|
|
updateRelayUrl(host, port)
|
|
await checkRelayReachable(host, port, token)
|
|
}
|
|
|
|
document.getElementById('save').addEventListener('click', () => void save())
|
|
void load()
|