refactor: centralize dashboard url + ws close code

This commit is contained in:
Peter Steinberger
2026-01-14 23:42:12 +00:00
parent 983e1b2303
commit 50fa106d87
3 changed files with 42 additions and 30 deletions

View File

@@ -388,6 +388,43 @@ actor GatewayEndpointStore {
}
}
extension GatewayEndpointStore {
static func dashboardURL(for config: GatewayConnection.Config) throws -> URL {
guard var components = URLComponents(url: config.url, resolvingAgainstBaseURL: false) else {
throw NSError(domain: "Dashboard", code: 1, userInfo: [
NSLocalizedDescriptionKey: "Invalid gateway URL",
])
}
switch components.scheme?.lowercased() {
case "ws":
components.scheme = "http"
case "wss":
components.scheme = "https"
default:
components.scheme = "http"
}
components.path = "/"
var queryItems: [URLQueryItem] = []
if let token = config.token?.trimmingCharacters(in: .whitespacesAndNewlines),
!token.isEmpty
{
queryItems.append(URLQueryItem(name: "token", value: token))
}
if let password = config.password?.trimmingCharacters(in: .whitespacesAndNewlines),
!password.isEmpty
{
queryItems.append(URLQueryItem(name: "password", value: password))
}
components.queryItems = queryItems.isEmpty ? nil : queryItems
guard let url = components.url else {
throw NSError(domain: "Dashboard", code: 2, userInfo: [
NSLocalizedDescriptionKey: "Failed to build dashboard URL",
])
}
return url
}
}
#if DEBUG
extension GatewayEndpointStore {
static func _testResolveGatewayPassword(

View File

@@ -313,34 +313,7 @@ struct MenuContent: View {
private func openDashboard() async {
do {
let config = try await GatewayEndpointStore.shared.requireConfig()
let wsURL = config.url
guard var components = URLComponents(url: wsURL, resolvingAgainstBaseURL: false) else {
throw NSError(domain: "Dashboard", code: 1, userInfo: [
NSLocalizedDescriptionKey: "Invalid gateway URL",
])
}
switch components.scheme?.lowercased() {
case "ws":
components.scheme = "http"
case "wss":
components.scheme = "https"
default:
components.scheme = "http"
}
components.path = "/"
var queryItems: [URLQueryItem] = []
if let token = config.token, !token.isEmpty {
queryItems.append(URLQueryItem(name: "token", value: token))
}
if let password = config.password, !password.isEmpty {
queryItems.append(URLQueryItem(name: "password", value: password))
}
components.queryItems = queryItems.isEmpty ? nil : queryItems
guard let url = components.url else {
throw NSError(domain: "Dashboard", code: 2, userInfo: [
NSLocalizedDescriptionKey: "Failed to build dashboard URL",
])
}
let url = try GatewayEndpointStore.dashboardURL(for: config)
NSWorkspace.shared.open(url)
} catch {
let alert = NSAlert()

View File

@@ -50,6 +50,9 @@ export type GatewayBrowserClientOptions = {
onGap?: (info: { expected: number; received: number }) => void;
};
// 4008 = application-defined code (browser rejects 1008 "Policy Violation")
const CONNECT_FAILED_CLOSE_CODE = 4008;
export class GatewayBrowserClient {
private ws: WebSocket | null = null;
private pending = new Map<string, Pending>();
@@ -134,8 +137,7 @@ export class GatewayBrowserClient {
this.opts.onHello?.(hello);
})
.catch(() => {
// 4008 = application-defined code (browser rejects 1008 "Policy Violation")
this.ws?.close(4008, "connect failed");
this.ws?.close(CONNECT_FAILED_CLOSE_CODE, "connect failed");
});
}