refactor: centralize dashboard url + ws close code
This commit is contained in:
@@ -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
|
#if DEBUG
|
||||||
extension GatewayEndpointStore {
|
extension GatewayEndpointStore {
|
||||||
static func _testResolveGatewayPassword(
|
static func _testResolveGatewayPassword(
|
||||||
|
|||||||
@@ -313,34 +313,7 @@ struct MenuContent: View {
|
|||||||
private func openDashboard() async {
|
private func openDashboard() async {
|
||||||
do {
|
do {
|
||||||
let config = try await GatewayEndpointStore.shared.requireConfig()
|
let config = try await GatewayEndpointStore.shared.requireConfig()
|
||||||
let wsURL = config.url
|
let url = try GatewayEndpointStore.dashboardURL(for: config)
|
||||||
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",
|
|
||||||
])
|
|
||||||
}
|
|
||||||
NSWorkspace.shared.open(url)
|
NSWorkspace.shared.open(url)
|
||||||
} catch {
|
} catch {
|
||||||
let alert = NSAlert()
|
let alert = NSAlert()
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ export type GatewayBrowserClientOptions = {
|
|||||||
onGap?: (info: { expected: number; received: number }) => void;
|
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 {
|
export class GatewayBrowserClient {
|
||||||
private ws: WebSocket | null = null;
|
private ws: WebSocket | null = null;
|
||||||
private pending = new Map<string, Pending>();
|
private pending = new Map<string, Pending>();
|
||||||
@@ -134,8 +137,7 @@ export class GatewayBrowserClient {
|
|||||||
this.opts.onHello?.(hello);
|
this.opts.onHello?.(hello);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// 4008 = application-defined code (browser rejects 1008 "Policy Violation")
|
this.ws?.close(CONNECT_FAILED_CLOSE_CODE, "connect failed");
|
||||||
this.ws?.close(4008, "connect failed");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user